diff --git a/.gitignore b/.gitignore index 323f06468b9..0ea4675a737 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ user_guide_src/build/* user_guide_src/cilexer/build/* user_guide_src/cilexer/dist/* user_guide_src/cilexer/pycilexer.egg-info/* -/vendor/ + # IDE Files #------------------------- diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000000..c0805f0d59d --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,72 @@ +pipeline { + agent any + + environment { + CI_ENV = 'production' + } + + stages { + stage('Checkout') { + steps { + git branch: 'main', url: 'https://github.com/callmevan1120/CodeIgniter.git' + } + } + + stage('Install Dependencies') { + steps { + sh ''' + # Install dependencies + composer install --no-dev --optimize-autoloader + + composer update + + # Pastikan package mikey179/vfsStream terinstal + if ! composer show | grep -q "mikey179/vfsStream"; then + echo "Package mikey179/vfsStream tidak ditemukan, menginstal..." + composer require --dev mikey179/vfsStream + fi + + # File yang akan diubah + FILE="mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStream.php" + + # Periksa apakah file ada sebelum menjalankan sed + if [ -f "$FILE" ]; then + sed -i.bak s/name{0}/name[0]/ "$FILE" + else + echo "File $FILE tidak ditemukan, melewati perintah sed." + fi + ''' + } + } + + stage('Run Tests') { + steps { + sh 'phpunit' + } + post { + success { + junit 'application/tests/results/*.xml' + } + failure { + echo 'Tests failed!' + } + } + } + + stage('Deploy') { + steps { + echo 'Deploying to production environment...' + // Tambahkan perintah deploy sesuai kebutuhan Anda + } + } + } + + post { + success { + echo 'Pipeline completed successfully!' + } + failure { + echo 'Pipeline failed!' + } + } +} diff --git a/application/tests/Example_test.php b/application/tests/Example_test.php new file mode 100644 index 00000000000..62a5977afc4 --- /dev/null +++ b/application/tests/Example_test.php @@ -0,0 +1,6 @@ +assertTrue(true); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index f5b4e8db43d..302e3c1140e 100644 --- a/composer.json +++ b/composer.json @@ -22,14 +22,14 @@ "phpunit --color=always --coverage-text --configuration tests/travis/sqlite.phpunit.xml" ], "post-install-cmd": [ - "sed -i s/name{0}/name[0]/ vendor/mikey179/vfsstream/src/main/php/org/bovigo/vfs/vfsStream.php" + "sed -i s/name{0}/name[0]/ vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStream.php" ], "post-update-cmd": [ - "sed -i s/name{0}/name[0]/ vendor/mikey179/vfsstream/src/main/php/org/bovigo/vfs/vfsStream.php" + "sed -i s/name{0}/name[0]/ vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStream.php" ] }, "require-dev": { - "mikey179/vfsstream": "1.6.*", - "phpunit/phpunit": "4.* || 5.* || 9.*" + "mikey179/vfsstream": "1.1.*", + "phpunit/phpunit": "4.* || 5.*" } } diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 00000000000..bffe4bdb04a --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ + + * 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, PSR-4 and classmap class loader. + * + * $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 + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + private $classMapAuthoritative = false; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + 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 PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $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 bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $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 bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative) { + return false; + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 00000000000..1a28124886d --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) 2016 Nils Adermann, 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/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000000..0a3cacbb3f1 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,175 @@ + $vendorDir . '/smarty/smarty/libs/Smarty.class.php', + 'SmartyBC' => $vendorDir . '/smarty/smarty/libs/SmartyBC.class.php', + 'SmartyCompilerException' => $vendorDir . '/smarty/smarty/libs/sysplugins/smartycompilerexception.php', + 'SmartyException' => $vendorDir . '/smarty/smarty/libs/sysplugins/smartyexception.php', + 'Smarty_CacheResource' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_cacheresource.php', + 'Smarty_CacheResource_Custom' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_cacheresource_custom.php', + 'Smarty_CacheResource_KeyValueStore' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_cacheresource_keyvaluestore.php', + 'Smarty_Data' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_data.php', + 'Smarty_Internal_CacheResource_File' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_cacheresource_file.php', + 'Smarty_Internal_CompileBase' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compilebase.php', + 'Smarty_Internal_Compile_Append' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_append.php', + 'Smarty_Internal_Compile_Assign' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_assign.php', + 'Smarty_Internal_Compile_Block' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_block.php', + 'Smarty_Internal_Compile_Blockclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_block.php', + 'Smarty_Internal_Compile_Break' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_break.php', + 'Smarty_Internal_Compile_Call' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_call.php', + 'Smarty_Internal_Compile_Capture' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_capture.php', + 'Smarty_Internal_Compile_CaptureClose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_capture.php', + 'Smarty_Internal_Compile_Config_Load' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_config_load.php', + 'Smarty_Internal_Compile_Continue' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_continue.php', + 'Smarty_Internal_Compile_Debug' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_debug.php', + 'Smarty_Internal_Compile_Else' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Elseif' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Eval' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_eval.php', + 'Smarty_Internal_Compile_Extends' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_extends.php', + 'Smarty_Internal_Compile_For' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_for.php', + 'Smarty_Internal_Compile_Forclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_for.php', + 'Smarty_Internal_Compile_Foreach' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_foreach.php', + 'Smarty_Internal_Compile_Foreachclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_foreach.php', + 'Smarty_Internal_Compile_Foreachelse' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_foreach.php', + 'Smarty_Internal_Compile_Forelse' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_for.php', + 'Smarty_Internal_Compile_Function' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_function.php', + 'Smarty_Internal_Compile_Functionclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_function.php', + 'Smarty_Internal_Compile_If' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Ifclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Include' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_include.php', + 'Smarty_Internal_Compile_Include_Php' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_include_php.php', + 'Smarty_Internal_Compile_Insert' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_insert.php', + 'Smarty_Internal_Compile_Ldelim' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_ldelim.php', + 'Smarty_Internal_Compile_Nocache' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_nocache.php', + 'Smarty_Internal_Compile_Nocacheclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_nocache.php', + 'Smarty_Internal_Compile_Private_Block_Plugin' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_block_plugin.php', + 'Smarty_Internal_Compile_Private_ForeachSection' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_foreachsection.php', + 'Smarty_Internal_Compile_Private_Function_Plugin' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_function_plugin.php', + 'Smarty_Internal_Compile_Private_Modifier' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_modifier.php', + 'Smarty_Internal_Compile_Private_Object_Block_Function' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_object_block_function.php', + 'Smarty_Internal_Compile_Private_Object_Function' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_object_function.php', + 'Smarty_Internal_Compile_Private_Php' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_php.php', + 'Smarty_Internal_Compile_Private_Print_Expression' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_print_expression.php', + 'Smarty_Internal_Compile_Private_Registered_Block' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_registered_block.php', + 'Smarty_Internal_Compile_Private_Registered_Function' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_registered_function.php', + 'Smarty_Internal_Compile_Private_Special_Variable' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_special_variable.php', + 'Smarty_Internal_Compile_Rdelim' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_rdelim.php', + 'Smarty_Internal_Compile_Section' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_section.php', + 'Smarty_Internal_Compile_Sectionclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_section.php', + 'Smarty_Internal_Compile_Sectionelse' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_section.php', + 'Smarty_Internal_Compile_Setfilter' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_setfilter.php', + 'Smarty_Internal_Compile_Setfilterclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_setfilter.php', + 'Smarty_Internal_Compile_Shared_Inheritance' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_shared_inheritance.php', + 'Smarty_Internal_Compile_While' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_while.php', + 'Smarty_Internal_Compile_Whileclose' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_while.php', + 'Smarty_Internal_Config_File_Compiler' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_config_file_compiler.php', + 'Smarty_Internal_Configfilelexer' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_configfilelexer.php', + 'Smarty_Internal_Configfileparser' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php', + 'Smarty_Internal_Data' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_data.php', + 'Smarty_Internal_Debug' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_debug.php', + 'Smarty_Internal_Extension_Clear' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_extension_clear.php', + 'Smarty_Internal_Extension_Handler' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_extension_handler.php', + 'Smarty_Internal_Method_AddAutoloadFilters' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_addautoloadfilters.php', + 'Smarty_Internal_Method_AddDefaultModifiers' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_adddefaultmodifiers.php', + 'Smarty_Internal_Method_Append' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_append.php', + 'Smarty_Internal_Method_AppendByRef' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_appendbyref.php', + 'Smarty_Internal_Method_AssignByRef' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_assignbyref.php', + 'Smarty_Internal_Method_AssignGlobal' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_assignglobal.php', + 'Smarty_Internal_Method_ClearAllAssign' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearallassign.php', + 'Smarty_Internal_Method_ClearAllCache' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearallcache.php', + 'Smarty_Internal_Method_ClearAssign' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearassign.php', + 'Smarty_Internal_Method_ClearCache' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearcache.php', + 'Smarty_Internal_Method_ClearCompiledTemplate' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearcompiledtemplate.php', + 'Smarty_Internal_Method_ClearConfig' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearconfig.php', + 'Smarty_Internal_Method_CompileAllConfig' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_compileallconfig.php', + 'Smarty_Internal_Method_CompileAllTemplates' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_compilealltemplates.php', + 'Smarty_Internal_Method_ConfigLoad' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_configload.php', + 'Smarty_Internal_Method_CreateData' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_createdata.php', + 'Smarty_Internal_Method_GetAutoloadFilters' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getautoloadfilters.php', + 'Smarty_Internal_Method_GetConfigVars' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getconfigvars.php', + 'Smarty_Internal_Method_GetDebugTemplate' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getdebugtemplate.php', + 'Smarty_Internal_Method_GetDefaultModifiers' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getdefaultmodifiers.php', + 'Smarty_Internal_Method_GetRegisteredObject' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getregisteredobject.php', + 'Smarty_Internal_Method_GetStreamVariable' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getstreamvariable.php', + 'Smarty_Internal_Method_GetTags' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_gettags.php', + 'Smarty_Internal_Method_GetTemplateVars' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_gettemplatevars.php', + 'Smarty_Internal_Method_LoadFilter' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_loadfilter.php', + 'Smarty_Internal_Method_LoadPlugin' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_loadplugin.php', + 'Smarty_Internal_Method_MustCompile' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_mustcompile.php', + 'Smarty_Internal_Method_RegisterCacheResource' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registercacheresource.php', + 'Smarty_Internal_Method_RegisterClass' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerclass.php', + 'Smarty_Internal_Method_RegisterDefaultConfigHandler' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerdefaultconfighandler.php', + 'Smarty_Internal_Method_RegisterDefaultPluginHandler' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php', + 'Smarty_Internal_Method_RegisterDefaultTemplateHandler' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerdefaulttemplatehandler.php', + 'Smarty_Internal_Method_RegisterFilter' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerfilter.php', + 'Smarty_Internal_Method_RegisterObject' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerobject.php', + 'Smarty_Internal_Method_RegisterPlugin' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerplugin.php', + 'Smarty_Internal_Method_RegisterResource' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerresource.php', + 'Smarty_Internal_Method_SetAutoloadFilters' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_setautoloadfilters.php', + 'Smarty_Internal_Method_SetDebugTemplate' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_setdebugtemplate.php', + 'Smarty_Internal_Method_SetDefaultModifiers' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_setdefaultmodifiers.php', + 'Smarty_Internal_Method_UnloadFilter' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unloadfilter.php', + 'Smarty_Internal_Method_UnregisterCacheResource' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregistercacheresource.php', + 'Smarty_Internal_Method_UnregisterFilter' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterfilter.php', + 'Smarty_Internal_Method_UnregisterObject' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterobject.php', + 'Smarty_Internal_Method_UnregisterPlugin' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterplugin.php', + 'Smarty_Internal_Method_UnregisterResource' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterresource.php', + 'Smarty_Internal_Nocache_Insert' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_nocache_insert.php', + 'Smarty_Internal_ParseTree' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree.php', + 'Smarty_Internal_ParseTree_Code' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_code.php', + 'Smarty_Internal_ParseTree_Dq' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_dq.php', + 'Smarty_Internal_ParseTree_DqContent' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_dqcontent.php', + 'Smarty_Internal_ParseTree_Tag' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_tag.php', + 'Smarty_Internal_ParseTree_Template' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_template.php', + 'Smarty_Internal_ParseTree_Text' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_text.php', + 'Smarty_Internal_Resource_Eval' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_eval.php', + 'Smarty_Internal_Resource_Extends' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_extends.php', + 'Smarty_Internal_Resource_File' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_file.php', + 'Smarty_Internal_Resource_Php' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_php.php', + 'Smarty_Internal_Resource_Registered' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_registered.php', + 'Smarty_Internal_Resource_Stream' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_stream.php', + 'Smarty_Internal_Resource_String' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_string.php', + 'Smarty_Internal_Runtime_CacheModify' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_cachemodify.php', + 'Smarty_Internal_Runtime_CodeFrame' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_codeframe.php', + 'Smarty_Internal_Runtime_FilterHandler' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_filterhandler.php', + 'Smarty_Internal_Runtime_Foreach' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_foreach.php', + 'Smarty_Internal_Runtime_GetIncludePath' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_getincludepath.php', + 'Smarty_Internal_Runtime_Hhvm' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_hhvm.php', + 'Smarty_Internal_Runtime_Inheritance' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_inheritance.php', + 'Smarty_Internal_Runtime_SubTemplate' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_subtemplate.php', + 'Smarty_Internal_Runtime_TplFunction' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_tplfunction.php', + 'Smarty_Internal_Runtime_UpdateCache' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_updatecache.php', + 'Smarty_Internal_Runtime_UpdateScope' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_updatescope.php', + 'Smarty_Internal_Runtime_ValidateCompiled' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_validatecompiled.php', + 'Smarty_Internal_Runtime_Var' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_var.php', + 'Smarty_Internal_Runtime_WriteFile' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_writefile.php', + 'Smarty_Internal_SmartyTemplateCompiler' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_smartytemplatecompiler.php', + 'Smarty_Internal_Template' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_template.php', + 'Smarty_Internal_TemplateBase' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templatebase.php', + 'Smarty_Internal_TemplateCompilerBase' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templatecompilerbase.php', + 'Smarty_Internal_Templatelexer' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templatelexer.php', + 'Smarty_Internal_Templateparser' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php', + 'Smarty_Internal_TestInstall' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_testinstall.php', + 'Smarty_Internal_Undefined' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_undefined.php', + 'Smarty_Resource' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_resource.php', + 'Smarty_Resource_Custom' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_resource_custom.php', + 'Smarty_Resource_Recompiled' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_resource_recompiled.php', + 'Smarty_Resource_Uncompiled' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_resource_uncompiled.php', + 'Smarty_Security' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_security.php', + 'Smarty_Template_Cached' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_template_cached.php', + 'Smarty_Template_Compiled' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_template_compiled.php', + 'Smarty_Template_Config' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_template_config.php', + 'Smarty_Template_Resource_Base' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_template_resource_base.php', + 'Smarty_Template_Source' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_template_source.php', + 'Smarty_Undefined_Variable' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_undefined_variable.php', + 'Smarty_Variable' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_variable.php', + 'TPC_yyStackEntry' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php', + 'TPC_yyToken' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php', + 'TP_yyStackEntry' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php', + 'TP_yyToken' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php', +); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php new file mode 100644 index 00000000000..315ab83bcd1 --- /dev/null +++ b/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000000..7c3b359509e --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,17 @@ + array($vendorDir . '/mikey179/vfsStream/src/main/php'), + 'Requests' => array($vendorDir . '/rmccue/requests/library'), + 'Doctrine\\ORM\\' => array($vendorDir . '/doctrine/orm/lib'), + 'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/lib'), + 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib'), + 'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib'), + 'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'), + 'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib'), +); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php new file mode 100644 index 00000000000..9d20ba71d73 --- /dev/null +++ b/vendor/composer/autoload_psr4.php @@ -0,0 +1,14 @@ + array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), + 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), + 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib/Doctrine/Common'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 00000000000..d44995b35c9 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,70 @@ += 50600 && !defined('HHVM_VERSION'); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit07478d198fc3d0499f102aefd1275be5::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit07478d198fc3d0499f102aefd1275be5::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire07478d198fc3d0499f102aefd1275be5($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequire07478d198fc3d0499f102aefd1275be5($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 00000000000..ca4b5d2d60a --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,273 @@ + __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'S' => + array ( + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Component\\Console\\' => 26, + ), + 'D' => + array ( + 'Doctrine\\Instantiator\\' => 22, + 'Doctrine\\Common\\Cache\\' => 22, + 'Doctrine\\Common\\' => 16, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Component\\Console\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/console', + ), + 'Doctrine\\Instantiator\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator', + ), + 'Doctrine\\Common\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache', + ), + 'Doctrine\\Common\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/common/lib/Doctrine/Common', + ), + ); + + public static $prefixesPsr0 = array ( + 'o' => + array ( + 'org\\bovigo\\vfs' => + array ( + 0 => __DIR__ . '/..' . '/mikey179/vfsStream/src/main/php', + ), + ), + 'R' => + array ( + 'Requests' => + array ( + 0 => __DIR__ . '/..' . '/rmccue/requests/library', + ), + ), + 'D' => + array ( + 'Doctrine\\ORM\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/orm/lib', + ), + 'Doctrine\\DBAL\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/dbal/lib', + ), + 'Doctrine\\Common\\Lexer\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/lexer/lib', + ), + 'Doctrine\\Common\\Inflector\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/inflector/lib', + ), + 'Doctrine\\Common\\Collections\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/collections/lib', + ), + 'Doctrine\\Common\\Annotations\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/annotations/lib', + ), + ), + ); + + public static $classMap = array ( + 'Smarty' => __DIR__ . '/..' . '/smarty/smarty/libs/Smarty.class.php', + 'SmartyBC' => __DIR__ . '/..' . '/smarty/smarty/libs/SmartyBC.class.php', + 'SmartyCompilerException' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smartycompilerexception.php', + 'SmartyException' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smartyexception.php', + 'Smarty_CacheResource' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_cacheresource.php', + 'Smarty_CacheResource_Custom' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_cacheresource_custom.php', + 'Smarty_CacheResource_KeyValueStore' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_cacheresource_keyvaluestore.php', + 'Smarty_Data' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_data.php', + 'Smarty_Internal_CacheResource_File' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_cacheresource_file.php', + 'Smarty_Internal_CompileBase' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compilebase.php', + 'Smarty_Internal_Compile_Append' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_append.php', + 'Smarty_Internal_Compile_Assign' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_assign.php', + 'Smarty_Internal_Compile_Block' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_block.php', + 'Smarty_Internal_Compile_Blockclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_block.php', + 'Smarty_Internal_Compile_Break' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_break.php', + 'Smarty_Internal_Compile_Call' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_call.php', + 'Smarty_Internal_Compile_Capture' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_capture.php', + 'Smarty_Internal_Compile_CaptureClose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_capture.php', + 'Smarty_Internal_Compile_Config_Load' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_config_load.php', + 'Smarty_Internal_Compile_Continue' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_continue.php', + 'Smarty_Internal_Compile_Debug' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_debug.php', + 'Smarty_Internal_Compile_Else' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Elseif' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Eval' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_eval.php', + 'Smarty_Internal_Compile_Extends' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_extends.php', + 'Smarty_Internal_Compile_For' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_for.php', + 'Smarty_Internal_Compile_Forclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_for.php', + 'Smarty_Internal_Compile_Foreach' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_foreach.php', + 'Smarty_Internal_Compile_Foreachclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_foreach.php', + 'Smarty_Internal_Compile_Foreachelse' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_foreach.php', + 'Smarty_Internal_Compile_Forelse' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_for.php', + 'Smarty_Internal_Compile_Function' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_function.php', + 'Smarty_Internal_Compile_Functionclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_function.php', + 'Smarty_Internal_Compile_If' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Ifclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_if.php', + 'Smarty_Internal_Compile_Include' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_include.php', + 'Smarty_Internal_Compile_Include_Php' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_include_php.php', + 'Smarty_Internal_Compile_Insert' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_insert.php', + 'Smarty_Internal_Compile_Ldelim' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_ldelim.php', + 'Smarty_Internal_Compile_Nocache' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_nocache.php', + 'Smarty_Internal_Compile_Nocacheclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_nocache.php', + 'Smarty_Internal_Compile_Private_Block_Plugin' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_block_plugin.php', + 'Smarty_Internal_Compile_Private_ForeachSection' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_foreachsection.php', + 'Smarty_Internal_Compile_Private_Function_Plugin' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_function_plugin.php', + 'Smarty_Internal_Compile_Private_Modifier' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_modifier.php', + 'Smarty_Internal_Compile_Private_Object_Block_Function' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_object_block_function.php', + 'Smarty_Internal_Compile_Private_Object_Function' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_object_function.php', + 'Smarty_Internal_Compile_Private_Php' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_php.php', + 'Smarty_Internal_Compile_Private_Print_Expression' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_print_expression.php', + 'Smarty_Internal_Compile_Private_Registered_Block' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_registered_block.php', + 'Smarty_Internal_Compile_Private_Registered_Function' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_registered_function.php', + 'Smarty_Internal_Compile_Private_Special_Variable' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_private_special_variable.php', + 'Smarty_Internal_Compile_Rdelim' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_rdelim.php', + 'Smarty_Internal_Compile_Section' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_section.php', + 'Smarty_Internal_Compile_Sectionclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_section.php', + 'Smarty_Internal_Compile_Sectionelse' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_section.php', + 'Smarty_Internal_Compile_Setfilter' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_setfilter.php', + 'Smarty_Internal_Compile_Setfilterclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_setfilter.php', + 'Smarty_Internal_Compile_Shared_Inheritance' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_shared_inheritance.php', + 'Smarty_Internal_Compile_While' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_while.php', + 'Smarty_Internal_Compile_Whileclose' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_compile_while.php', + 'Smarty_Internal_Config_File_Compiler' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_config_file_compiler.php', + 'Smarty_Internal_Configfilelexer' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_configfilelexer.php', + 'Smarty_Internal_Configfileparser' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php', + 'Smarty_Internal_Data' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_data.php', + 'Smarty_Internal_Debug' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_debug.php', + 'Smarty_Internal_Extension_Clear' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_extension_clear.php', + 'Smarty_Internal_Extension_Handler' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_extension_handler.php', + 'Smarty_Internal_Method_AddAutoloadFilters' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_addautoloadfilters.php', + 'Smarty_Internal_Method_AddDefaultModifiers' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_adddefaultmodifiers.php', + 'Smarty_Internal_Method_Append' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_append.php', + 'Smarty_Internal_Method_AppendByRef' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_appendbyref.php', + 'Smarty_Internal_Method_AssignByRef' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_assignbyref.php', + 'Smarty_Internal_Method_AssignGlobal' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_assignglobal.php', + 'Smarty_Internal_Method_ClearAllAssign' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearallassign.php', + 'Smarty_Internal_Method_ClearAllCache' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearallcache.php', + 'Smarty_Internal_Method_ClearAssign' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearassign.php', + 'Smarty_Internal_Method_ClearCache' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearcache.php', + 'Smarty_Internal_Method_ClearCompiledTemplate' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearcompiledtemplate.php', + 'Smarty_Internal_Method_ClearConfig' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_clearconfig.php', + 'Smarty_Internal_Method_CompileAllConfig' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_compileallconfig.php', + 'Smarty_Internal_Method_CompileAllTemplates' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_compilealltemplates.php', + 'Smarty_Internal_Method_ConfigLoad' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_configload.php', + 'Smarty_Internal_Method_CreateData' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_createdata.php', + 'Smarty_Internal_Method_GetAutoloadFilters' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getautoloadfilters.php', + 'Smarty_Internal_Method_GetConfigVars' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getconfigvars.php', + 'Smarty_Internal_Method_GetDebugTemplate' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getdebugtemplate.php', + 'Smarty_Internal_Method_GetDefaultModifiers' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getdefaultmodifiers.php', + 'Smarty_Internal_Method_GetRegisteredObject' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getregisteredobject.php', + 'Smarty_Internal_Method_GetStreamVariable' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_getstreamvariable.php', + 'Smarty_Internal_Method_GetTags' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_gettags.php', + 'Smarty_Internal_Method_GetTemplateVars' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_gettemplatevars.php', + 'Smarty_Internal_Method_LoadFilter' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_loadfilter.php', + 'Smarty_Internal_Method_LoadPlugin' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_loadplugin.php', + 'Smarty_Internal_Method_MustCompile' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_mustcompile.php', + 'Smarty_Internal_Method_RegisterCacheResource' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registercacheresource.php', + 'Smarty_Internal_Method_RegisterClass' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerclass.php', + 'Smarty_Internal_Method_RegisterDefaultConfigHandler' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerdefaultconfighandler.php', + 'Smarty_Internal_Method_RegisterDefaultPluginHandler' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php', + 'Smarty_Internal_Method_RegisterDefaultTemplateHandler' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerdefaulttemplatehandler.php', + 'Smarty_Internal_Method_RegisterFilter' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerfilter.php', + 'Smarty_Internal_Method_RegisterObject' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerobject.php', + 'Smarty_Internal_Method_RegisterPlugin' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerplugin.php', + 'Smarty_Internal_Method_RegisterResource' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_registerresource.php', + 'Smarty_Internal_Method_SetAutoloadFilters' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_setautoloadfilters.php', + 'Smarty_Internal_Method_SetDebugTemplate' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_setdebugtemplate.php', + 'Smarty_Internal_Method_SetDefaultModifiers' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_setdefaultmodifiers.php', + 'Smarty_Internal_Method_UnloadFilter' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unloadfilter.php', + 'Smarty_Internal_Method_UnregisterCacheResource' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregistercacheresource.php', + 'Smarty_Internal_Method_UnregisterFilter' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterfilter.php', + 'Smarty_Internal_Method_UnregisterObject' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterobject.php', + 'Smarty_Internal_Method_UnregisterPlugin' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterplugin.php', + 'Smarty_Internal_Method_UnregisterResource' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_method_unregisterresource.php', + 'Smarty_Internal_Nocache_Insert' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_nocache_insert.php', + 'Smarty_Internal_ParseTree' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree.php', + 'Smarty_Internal_ParseTree_Code' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_code.php', + 'Smarty_Internal_ParseTree_Dq' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_dq.php', + 'Smarty_Internal_ParseTree_DqContent' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_dqcontent.php', + 'Smarty_Internal_ParseTree_Tag' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_tag.php', + 'Smarty_Internal_ParseTree_Template' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_template.php', + 'Smarty_Internal_ParseTree_Text' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_parsetree_text.php', + 'Smarty_Internal_Resource_Eval' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_eval.php', + 'Smarty_Internal_Resource_Extends' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_extends.php', + 'Smarty_Internal_Resource_File' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_file.php', + 'Smarty_Internal_Resource_Php' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_php.php', + 'Smarty_Internal_Resource_Registered' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_registered.php', + 'Smarty_Internal_Resource_Stream' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_stream.php', + 'Smarty_Internal_Resource_String' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_resource_string.php', + 'Smarty_Internal_Runtime_CacheModify' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_cachemodify.php', + 'Smarty_Internal_Runtime_CodeFrame' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_codeframe.php', + 'Smarty_Internal_Runtime_FilterHandler' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_filterhandler.php', + 'Smarty_Internal_Runtime_Foreach' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_foreach.php', + 'Smarty_Internal_Runtime_GetIncludePath' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_getincludepath.php', + 'Smarty_Internal_Runtime_Hhvm' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_hhvm.php', + 'Smarty_Internal_Runtime_Inheritance' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_inheritance.php', + 'Smarty_Internal_Runtime_SubTemplate' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_subtemplate.php', + 'Smarty_Internal_Runtime_TplFunction' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_tplfunction.php', + 'Smarty_Internal_Runtime_UpdateCache' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_updatecache.php', + 'Smarty_Internal_Runtime_UpdateScope' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_updatescope.php', + 'Smarty_Internal_Runtime_ValidateCompiled' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_validatecompiled.php', + 'Smarty_Internal_Runtime_Var' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_var.php', + 'Smarty_Internal_Runtime_WriteFile' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_runtime_writefile.php', + 'Smarty_Internal_SmartyTemplateCompiler' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_smartytemplatecompiler.php', + 'Smarty_Internal_Template' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_template.php', + 'Smarty_Internal_TemplateBase' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templatebase.php', + 'Smarty_Internal_TemplateCompilerBase' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templatecompilerbase.php', + 'Smarty_Internal_Templatelexer' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templatelexer.php', + 'Smarty_Internal_Templateparser' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php', + 'Smarty_Internal_TestInstall' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_testinstall.php', + 'Smarty_Internal_Undefined' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_undefined.php', + 'Smarty_Resource' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_resource.php', + 'Smarty_Resource_Custom' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_resource_custom.php', + 'Smarty_Resource_Recompiled' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_resource_recompiled.php', + 'Smarty_Resource_Uncompiled' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_resource_uncompiled.php', + 'Smarty_Security' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_security.php', + 'Smarty_Template_Cached' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_template_cached.php', + 'Smarty_Template_Compiled' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_template_compiled.php', + 'Smarty_Template_Config' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_template_config.php', + 'Smarty_Template_Resource_Base' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_template_resource_base.php', + 'Smarty_Template_Source' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_template_source.php', + 'Smarty_Undefined_Variable' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_undefined_variable.php', + 'Smarty_Variable' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_variable.php', + 'TPC_yyStackEntry' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php', + 'TPC_yyToken' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php', + 'TP_yyStackEntry' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php', + 'TP_yyToken' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit07478d198fc3d0499f102aefd1275be5::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit07478d198fc3d0499f102aefd1275be5::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit07478d198fc3d0499f102aefd1275be5::$prefixesPsr0; + $loader->classMap = ComposerStaticInit07478d198fc3d0499f102aefd1275be5::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 00000000000..911a6112375 --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,977 @@ +[ + { + "name": "mikey179/vfsStream", + "version": "v1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/mikey179/vfsStream.git", + "reference": "fc0fe8f4d0b527254a2dc45f0c265567c881d07e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/fc0fe8f4d0b527254a2dc45f0c265567c881d07e", + "reference": "fc0fe8f4d0b527254a2dc45f0c265567c881d07e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2012-08-25 12:49:29", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "org\\bovigo\\vfs": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "homepage": "http://vfs.bovigo.org/" + }, + { + "name": "kenjis/ci-phpunit-test", + "version": "v0.12.1", + "version_normalized": "0.12.1.0", + "source": { + "type": "git", + "url": "https://github.com/kenjis/ci-phpunit-test.git", + "reference": "00447300447a43b1da84de73125737d34ff3b360" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kenjis/ci-phpunit-test/zipball/00447300447a43b1da84de73125737d34ff3b360", + "reference": "00447300447a43b1da84de73125737d34ff3b360", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "replace": { + "nikic/php-parser": "2.0.1" + }, + "time": "2016-06-11 09:06:08", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kenji Suzuki", + "homepage": "https://github.com/kenjis" + } + ], + "description": "An easier way to use PHPUnit with CodeIgniter 3.0", + "homepage": "http://kenjis.github.io/ci-phpunit-test/", + "keywords": [ + "codeigniter", + "monkey patch", + "phpunit", + "test", + "unit testing" + ] + }, + { + "name": "rmccue/requests", + "version": "v1.6.1", + "version_normalized": "1.6.1.0", + "source": { + "type": "git", + "url": "https://github.com/rmccue/Requests.git", + "reference": "6aac485666c2955077d77b796bbdd25f0013a4ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rmccue/Requests/zipball/6aac485666c2955077d77b796bbdd25f0013a4ea", + "reference": "6aac485666c2955077d77b796bbdd25f0013a4ea", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "require-dev": { + "satooshi/php-coveralls": "dev-master" + }, + "time": "2014-05-18 04:59:02", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Requests": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Ryan McCue", + "homepage": "http://ryanmccue.info" + } + ], + "description": "A HTTP library written in PHP, for human beings.", + "homepage": "http://github.com/rmccue/Requests", + "keywords": [ + "curl", + "fsockopen", + "http", + "idna", + "ipv6", + "iri", + "sockets" + ] + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "dff51f72b0706335131b00a7f49606168c582594" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2016-05-18 14:26:46", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/console", + "version": "v3.1.2", + "version_normalized": "3.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "747154aa69b0f83cd02fc9aa554836dee417631a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/747154aa69b0f83cd02fc9aa554836dee417631a", + "reference": "747154aa69b0f83cd02fc9aa554836dee417631a", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "time": "2016-06-29 07:02:31", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com" + }, + { + "name": "doctrine/cache", + "version": "v1.6.0", + "version_normalized": "1.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "time": "2015-12-31 16:37:02", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ] + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "time": "2014-09-09 13:34:57", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ] + }, + { + "name": "doctrine/inflector", + "version": "v1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "time": "2015-11-06 14:35:42", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ] + }, + { + "name": "doctrine/collections", + "version": "v1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2015-04-14 22:21:58", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ] + }, + { + "name": "doctrine/annotations", + "version": "v1.2.7", + "version_normalized": "1.2.7.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "time": "2015-08-31 12:32:49", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ] + }, + { + "name": "doctrine/common", + "version": "v2.6.1", + "version_normalized": "2.6.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.5|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "time": "2015-12-25 13:18:31", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ] + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "version_normalized": "1.0.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "time": "2015-06-14 21:17:01", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ] + }, + { + "name": "doctrine/dbal", + "version": "v2.5.4", + "version_normalized": "2.5.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/abbdfd1cff43a7b99d027af3be709bc8fc7d4769", + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.7-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "time": "2016-01-05 22:11:12", + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ] + }, + { + "name": "doctrine/orm", + "version": "v2.5.4", + "version_normalized": "2.5.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "bc4ddbfb0114cb33438cc811c9a740d8aa304aab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/bc4ddbfb0114cb33438cc811c9a740d8aa304aab", + "reference": "bc4ddbfb0114cb33438cc811c9a740d8aa304aab", + "shasum": "" + }, + "require": { + "doctrine/cache": "~1.4", + "doctrine/collections": "~1.2", + "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "ext-pdo": "*", + "php": ">=5.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "symfony/yaml": "~2.3|~3.0" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "time": "2016-01-05 21:34:58", + "bin": [ + "bin/doctrine", + "bin/doctrine.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\ORM\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ] + }, + { + "name": "kenjis/codeigniter-doctrine", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/kenjis/codeigniter-doctrine.git", + "reference": "684ef3eceb4572276822c266a63228966bb96603" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kenjis/codeigniter-doctrine/zipball/684ef3eceb4572276822c266a63228966bb96603", + "reference": "684ef3eceb4572276822c266a63228966bb96603", + "shasum": "" + }, + "require": { + "doctrine/orm": "~2.5", + "php": ">=5.4.0" + }, + "require-dev": { + "codeigniter/framework": "3.0.*" + }, + "time": "2015-06-10 01:15:44", + "type": "project", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "source", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kenji Suzuki", + "homepage": "https://github.com/kenjis/codeigniter-doctrine" + } + ], + "description": "A simple Doctrine integration for CodeIgniter 3.0", + "keywords": [ + "codeigniter", + "doctrine" + ] + }, + { + "name": "smarty/smarty", + "version": "v3.1.29", + "version_normalized": "3.1.29.0", + "source": { + "type": "git", + "url": "https://github.com/smarty-php/smarty.git", + "reference": "35480f10e7ce9b0fdaf23d3799d7b79463919b1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/smarty-php/smarty/zipball/35480f10e7ce9b0fdaf23d3799d7b79463919b1e", + "reference": "35480f10e7ce9b0fdaf23d3799d7b79463919b1e", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "time": "2015-12-21 01:57:06", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "libs/Smarty.class.php", + "libs/SmartyBC.class.php", + "libs/sysplugins/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Monte Ohrt", + "email": "monte@ohrt.com" + }, + { + "name": "Uwe Tews", + "email": "uwe.tews@googlemail.com" + }, + { + "name": "Rodney Rehm", + "email": "rodney.rehm@medialize.de" + } + ], + "description": "Smarty - the compiling PHP template engine", + "homepage": "http://www.smarty.net", + "keywords": [ + "templating" + ] + } +] diff --git a/vendor/doctrine/annotations/LICENSE b/vendor/doctrine/annotations/LICENSE new file mode 100644 index 00000000000..5e781fce4bb --- /dev/null +++ b/vendor/doctrine/annotations/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 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/annotations/README.md b/vendor/doctrine/annotations/README.md new file mode 100644 index 00000000000..ebb30e0bca4 --- /dev/null +++ b/vendor/doctrine/annotations/README.md @@ -0,0 +1,19 @@ +# Doctrine Annotations + +[![Build Status](https://travis-ci.org/doctrine/annotations.png?branch=master)](https://travis-ci.org/doctrine/annotations) + +Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)). + +## Changelog + +### v1.2.0 + + * HHVM support + * Allowing dangling comma in annotations + * Excluded annotations are no longer autoloaded + * Importing namespaces also in traits + * Added support for `::class` 5.5-style constant, works also in 5.3 and 5.4 + +### v1.1 + + * Add Exception when ZendOptimizer+ or Opcache is configured to drop comments diff --git a/vendor/doctrine/annotations/composer.json b/vendor/doctrine/annotations/composer.json new file mode 100644 index 00000000000..1c65f6cd35a --- /dev/null +++ b/vendor/doctrine/annotations/composer.json @@ -0,0 +1,31 @@ +{ + "name": "doctrine/annotations", + "type": "library", + "description": "Docblock Annotations Parser", + "keywords": ["annotations", "docblock", "parser"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "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", + "doctrine/lexer": "1.*" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Annotations\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php new file mode 100644 index 00000000000..a79a0f8f0a1 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php @@ -0,0 +1,79 @@ +. + */ + +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. + * + * @throws \BadMethodCallException + */ + 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 Unknown property name. + * @param mixed $value Property value. + * + * @throws \BadMethodCallException + */ + public function __set($name, $value) + { + throw new \BadMethodCallException( + sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this)) + ); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php new file mode 100644 index 00000000000..dbef6df0874 --- /dev/null +++ b/vendor/doctrine/annotations/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; +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php new file mode 100644 index 00000000000..53134e3097a --- /dev/null +++ b/vendor/doctrine/annotations/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; +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php new file mode 100644 index 00000000000..e122a753588 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the available values during the parsing process. + * + * @since 2.4 + * @author Fabio B. Silva + * + * @Annotation + * @Attributes({ + * @Attribute("value", required = true, type = "array"), + * @Attribute("literal", required = false, type = "array") + * }) + */ +final class Enum +{ + /** + * @var array + */ + public $value; + + /** + * Literal target declaration. + * + * @var array + */ + public $literal; + + /** + * Annotation constructor. + * + * @param array $values + * + * @throws \InvalidArgumentException + */ + public function __construct(array $values) + { + if ( ! isset($values['literal'])) { + $values['literal'] = array(); + } + + foreach ($values['value'] as $var) { + if( ! is_scalar($var)) { + throw new \InvalidArgumentException(sprintf( + '@Enum supports only scalar values "%s" given.', + is_object($var) ? get_class($var) : gettype($var) + )); + } + } + + foreach ($values['literal'] as $key => $var) { + if( ! in_array($key, $values['value'])) { + throw new \InvalidArgumentException(sprintf( + 'Undefined enumerator value "%s" for literal "%s".', + $key , $var + )); + } + } + + $this->value = $values['value']; + $this->literal = $values['literal']; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php new file mode 100644 index 00000000000..175226a6715 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php @@ -0,0 +1,54 @@ +. + */ + +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 +{ + /** + * @var array + */ + public $names; + + /** + * Constructor. + * + * @param array $values + * + * @throws \RuntimeException + */ + 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']; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php new file mode 100644 index 00000000000..d67f9606879 --- /dev/null +++ b/vendor/doctrine/annotations/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 +{ +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php new file mode 100644 index 00000000000..f6c54453593 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php @@ -0,0 +1,107 @@ +. + */ + +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 constructor. + * + * @param array $values + * + * @throws \InvalidArgumentException + */ + 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); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php new file mode 100644 index 00000000000..d06fe663c26 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -0,0 +1,197 @@ +. + */ + +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 a type error. + * + * @since 1.1 + * + * @param string $message + * + * @return AnnotationException + */ + public static function typeError($message) + { + return new self('[Type Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a constant semantical error. + * + * @since 2.3 + * + * @param string $identifier + * @param string $context + * + * @return AnnotationException + */ + public static function semanticalErrorConstants($identifier, $context = null) + { + return self::semanticalError(sprintf( + "Couldn't find constant %s%s.", + $identifier, + $context ? ', ' . $context : '' + )); + } + + /** + * 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 attributeTypeError($attributeName, $annotationName, $context, $expected, $actual) + { + return self::typeError(sprintf( + '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 self::typeError(sprintf( + 'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.', + $attributeName, + $annotationName, + $context, + $expected + )); + } + + /** + * Creates a new AnnotationException describing a invalid enummerator. + * + * @since 2.4 + * + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param array $available + * @param mixed $given + * + * @return AnnotationException + */ + public static function enumeratorError($attributeName, $annotationName, $context, $available, $given) + { + return new self(sprintf( + '[Enum Error] Attribute "%s" of @%s declared on %s accept only [%s], but got %s.', + $attributeName, + $annotationName, + $context, + implode(', ', $available), + is_object($given) ? get_class($given) : $given + )); + } + + /** + * @return AnnotationException + */ + public static function optimizerPlusSaveComments() + { + return new self( + "You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1." + ); + } + + /** + * @return AnnotationException + */ + public static function optimizerPlusLoadComments() + { + return new self( + "You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1." + ); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php new file mode 100644 index 00000000000..4ebd1fb4a00 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php @@ -0,0 +1,394 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; +use Doctrine\Common\Annotations\Annotation\Target; +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 + */ +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( + // Annotation tags + 'Annotation' => true, 'Attribute' => true, 'Attributes' => true, + /* Can we enable this? 'Enum' => true, */ + 'Required' => true, + 'Target' => true, + // Widely used tags (but not existent in phpdoc) + 'fix' => true , 'fixme' => true, + 'override' => true, + // PHPDocumentor 1 tags + 'abstract'=> true, 'access'=> true, + 'code' => true, + 'deprec'=> true, + 'endcode' => true, 'exception'=> true, + 'final'=> true, + 'ingroup' => true, 'inheritdoc'=> true, 'inheritDoc'=> true, + 'magic' => true, + 'name'=> true, + 'toc' => true, 'tutorial'=> true, + 'private' => true, + 'static'=> true, 'staticvar'=> true, 'staticVar'=> true, + 'throw' => true, + // PHPDocumentor 2 tags. + 'api' => true, 'author'=> true, + 'category'=> true, 'copyright'=> true, + 'deprecated'=> true, + 'example'=> true, + 'filesource'=> true, + 'global'=> true, + 'ignore'=> true, /* Can we enable this? 'index' => true, */ 'internal'=> true, + 'license'=> true, 'link'=> true, + 'method' => true, + 'package'=> true, 'param'=> true, 'property' => true, 'property-read' => true, 'property-write' => true, + 'return'=> true, + 'see'=> true, 'since'=> true, 'source' => true, 'subpackage'=> true, + 'throws'=> true, 'todo'=> true, 'TODO'=> true, + 'usedby'=> true, 'uses' => true, + 'var'=> true, 'version'=> true, + // PHPUnit tags + 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, + // PHPCheckStyle + 'SuppressWarnings' => true, + // PHPStorm + 'noinspection' => true, + // PEAR + 'package_version' => true, + // PlantUML + 'startuml' => true, 'enduml' => 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() + { + if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + if (PHP_VERSION_ID < 70000) { + if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.load_comments') === "0" || ini_get('opcache.load_comments') === "0")) { + throw AnnotationException::optimizerPlusLoadComments(); + } + + if (extension_loaded('Zend OPcache') && ini_get('opcache.load_comments') == 0) { + throw AnnotationException::optimizerPlusLoadComments(); + } + } + + 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; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(ReflectionClass $class) + { + $this->parser->setTarget(Target::TARGET_CLASS); + $this->parser->setImports($this->getClassImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + + $this->parser->setTarget(Target::TARGET_PROPERTY); + $this->parser->setImports($this->getPropertyImports($property)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($property->getDocComment(), $context); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + + $this->parser->setTarget(Target::TARGET_METHOD); + $this->parser->setImports($this->getMethodImports($method)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($method->getDocComment(), $context); + } + + /** + * {@inheritDoc} + */ + 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]; + } + + /** + * Retrieves imports. + * + * @param \ReflectionClass $class + * + * @return array + */ + private function getClassImports(ReflectionClass $class) + { + if (isset($this->imports[$name = $class->getName()])) { + return $this->imports[$name]; + } + + $this->collectParsingMetadata($class); + + return $this->imports[$name]; + } + + /** + * Retrieves imports for methods. + * + * @param \ReflectionMethod $method + * + * @return array + */ + private function getMethodImports(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $classImports = $this->getClassImports($class); + if (!method_exists($class, 'getTraits')) { + return $classImports; + } + + $traitImports = array(); + + foreach ($class->getTraits() as $trait) { + if ($trait->hasMethod($method->getName()) + && $trait->getFileName() === $method->getFileName() + ) { + $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); + } + } + + return array_merge($classImports, $traitImports); + } + + /** + * Retrieves imports for properties. + * + * @param \ReflectionProperty $property + * + * @return array + */ + private function getPropertyImports(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $classImports = $this->getClassImports($class); + if (!method_exists($class, 'getTraits')) { + return $classImports; + } + + $traitImports = array(); + + foreach ($class->getTraits() as $trait) { + if ($trait->hasProperty($property->getName())) { + $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); + } + } + + return array_merge($classImports, $traitImports); + } + + /** + * 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/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php new file mode 100644 index 00000000000..13ceb6348b3 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * AnnotationRegistry. + */ +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(); + + /** + * @return void + */ + static public function reset() + { + self::$autoloadNamespaces = array(); + self::$loaders = array(); + } + + /** + * Registers file. + * + * @param string $file + * + * @return void + */ + static public function registerFile($file) + { + require_once $file; + } + + /** + * Adds 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 + * + * @return void + */ + static public function registerAutoloadNamespace($namespace, $dirs = null) + { + self::$autoloadNamespaces[$namespace] = $dirs; + } + + /** + * Registers multiple namespaces. + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param array $namespaces + * + * @return void + */ + static public function registerAutoloadNamespaces(array $namespaces) + { + self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); + } + + /** + * Registers an autoloading callable 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 callable $callable + * + * @return void + * + * @throws \InvalidArgumentException + */ + static public function registerLoader($callable) + { + if (!is_callable($callable)) { + throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader()."); + } + self::$loaders[] = $callable; + } + + /** + * Autoloads an annotation class silently. + * + * @param string $class + * + * @return boolean + */ + 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 (is_file($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/annotations/lib/Doctrine/Common/Annotations/CachedReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php new file mode 100644 index 00000000000..e6dc59329a5 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php @@ -0,0 +1,235 @@ +. + */ + +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 +{ + /** + * @var string + */ + private static $CACHE_SALT = '@[Annot]'; + + /** + * @var Reader + */ + private $delegate; + + /** + * @var Cache + */ + private $cache; + + /** + * @var boolean + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + /** + * Constructor. + * + * @param Reader $reader + * @param Cache $cache + * @param bool $debug + */ + public function __construct(Reader $reader, Cache $cache, $debug = false) + { + $this->delegate = $reader; + $this->cache = $cache; + $this->debug = (boolean) $debug; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $cacheKey = $class->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getClassAnnotations($class); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $cacheKey = $class->getName().'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getPropertyAnnotations($property); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $cacheKey = $class->getName().'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getMethodAnnotations($method); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Clears loaded annotations. + * + * @return void + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } + + /** + * Fetches a value from the cache. + * + * @param string $rawCacheKey The cache key. + * @param \ReflectionClass $class The related class. + * + * @return mixed The cached value or false when the value is not in cache. + */ + private function fetchFromCache($rawCacheKey, \ReflectionClass $class) + { + $cacheKey = $rawCacheKey . self::$CACHE_SALT; + if (($data = $this->cache->fetch($cacheKey)) !== false) { + if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) { + return $data; + } + } + + return false; + } + + /** + * Saves a value to the cache. + * + * @param string $rawCacheKey The cache key. + * @param mixed $value The value. + * + * @return void + */ + private function saveToCache($rawCacheKey, $value) + { + $cacheKey = $rawCacheKey . self::$CACHE_SALT; + $this->cache->save($cacheKey, $value); + if ($this->debug) { + $this->cache->save('[C]'.$cacheKey, time()); + } + } + + /** + * Checks if the cache is fresh. + * + * @param string $cacheKey + * @param \ReflectionClass $class + * + * @return boolean + */ + private function isCacheFresh($cacheKey, \ReflectionClass $class) + { + if (false === $filename = $class->getFilename()) { + return true; + } + + return $this->cache->fetch('[C]'.$cacheKey) >= filemtime($filename); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php new file mode 100644 index 00000000000..d864540e002 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Lexer\AbstractLexer; + +/** + * 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 AbstractLexer +{ + const T_NONE = 1; + const T_INTEGER = 2; + const T_STRING = 3; + const T_FLOAT = 4; + + // All tokens that are also identifiers should be >= 100 + const T_IDENTIFIER = 100; + 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; + + /** + * @var array + */ + protected $noCase = array( + '@' => self::T_AT, + ',' => self::T_COMMA, + '(' => self::T_OPEN_PARENTHESIS, + ')' => self::T_CLOSE_PARENTHESIS, + '{' => self::T_OPEN_CURLY_BRACES, + '}' => self::T_CLOSE_CURLY_BRACES, + '=' => self::T_EQUALS, + ':' => self::T_COLON, + '\\' => self::T_NAMESPACE_SEPARATOR + ); + + /** + * @var array + */ + protected $withCase = array( + 'true' => self::T_TRUE, + 'false' => self::T_FALSE, + 'null' => self::T_NULL + ); + + /** + * {@inheritdoc} + */ + protected function getCatchablePatterns() + { + return array( + '[a-z_\\\][a-z0-9_\:\\\]*[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; + + if ($value[0] === '"') { + $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + } + + if (isset($this->noCase[$value])) { + return $this->noCase[$value]; + } + + if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) { + return self::T_IDENTIFIER; + } + + $lowerValue = strtolower($value); + + if (isset($this->withCase[$lowerValue])) { + return $this->withCase[$lowerValue]; + } + + // Checking numeric value + if (is_numeric($value)) { + return (strpos($value, '.') !== false || stripos($value, 'e') !== false) + ? self::T_FLOAT : self::T_INTEGER; + } + + return $type; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php new file mode 100644 index 00000000000..db668460ee1 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php @@ -0,0 +1,1138 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\Attribute; +use ReflectionClass; +use Doctrine\Common\Annotations\Annotation\Enum; +use Doctrine\Common\Annotations\Annotation\Target; +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' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Enum' => array( + 'is_annotation' => true, + 'has_constructor' => true, + 'targets_literal' => 'ANNOTATION_PROPERTY', + 'targets' => Target::TARGET_PROPERTY, + 'default_property' => 'value', + 'properties' => array( + 'value' => 'value' + ), + 'attribute_types' => array( + 'value' => array( + 'type' => 'array', + 'required' => true, + ), + 'literal' => array( + 'type' => 'array', + 'required' => false, + ), + ), + ), + ); + + /** + * 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 + * + * @return void + */ + public function setIgnoredAnnotationNames(array $names) + { + $this->ignoredAnnotationNames = $names; + } + + /** + * Sets ignore on not-imported annotations. + * + * @param boolean $bool + * + * @return void + */ + public function setIgnoreNotImportedAnnotations($bool) + { + $this->ignoreNotImportedAnnotations = (boolean) $bool; + } + + /** + * Sets the default namespaces. + * + * @param array $namespace + * + * @return void + * + * @throws \RuntimeException + */ + public function addNamespace($namespace) + { + if ($this->imports) { + throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); + } + + $this->namespaces[] = $namespace; + } + + /** + * Sets the imports. + * + * @param array $imports + * + * @return void + * + * @throws \RuntimeException + */ + 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 + * + * @return void + */ + 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 = '') + { + $pos = $this->findInitialTokenPosition($input); + if ($pos === null) { + return array(); + } + + $this->context = $context; + + $this->lexer->setInput(trim(substr($input, $pos), '* /')); + $this->lexer->moveNext(); + + return $this->Annotations(); + } + + /** + * Finds the first valid annotation + * + * @param string $input The docblock string to parse + * + * @return int|null + */ + private function findInitialTokenPosition($input) + { + $pos = 0; + + // search for first valid annotation + while (($pos = strpos($input, '@', $pos)) !== false) { + // if the @ is preceded by a space or * it is valid + if ($pos === 0 || $input[$pos - 1] === ' ' || $input[$pos - 1] === '*') { + return $pos; + } + + $pos++; + } + + return null; + } + + /** + * Attempts to match the given token with the current lookahead token. + * If they match, updates the lookahead token; otherwise raises a syntax error. + * + * @param integer $token Type of token. + * + * @return boolean 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 boolean + */ + 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|null $token Optional token. + * + * @return void + * + * @throws AnnotationException + */ + private function syntaxError($expected, $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $message = sprintf('Expected %s, got ', $expected); + $message .= ($this->lexer->lookahead === null) + ? 'end of string' + : sprintf("'%s' at position %s", $token['value'], $token['position']); + + if (strlen($this->context)) { + $message .= ' in ' . $this->context; + } + + $message .= '.'; + + throw AnnotationException::syntaxError($message); + } + + /** + * Attempts 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 + * + * @return void + */ + private function collectAnnotationMetadata($name) + { + if (self::$metadataParser === null) { + self::$metadataParser = new self(); + + self::$metadataParser->setIgnoreNotImportedAnnotations(true); + self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames); + self::$metadataParser->setImports(array( + 'enum' => 'Doctrine\Common\Annotations\Annotation\Enum', + 'target' => 'Doctrine\Common\Annotations\Annotation\Target', + 'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute', + 'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes' + )); + + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Enum.php'); + 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']) { + self::$metadataParser->setTarget(Target::TARGET_CLASS); + + foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { + if ($annotation instanceof Target) { + $metadata['targets'] = $annotation->targets; + $metadata['targets_literal'] = $annotation->literal; + + continue; + } + + if ($annotation instanceof Attributes) { + foreach ($annotation->value as $attribute) { + $this->collectAttributeTypeMetadata($metadata, $attribute); + } + } + } + + // 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; + + if (false === ($propertyComment = $property->getDocComment())) { + continue; + } + + $attribute = new Attribute(); + + $attribute->required = (false !== strpos($propertyComment, '@Required')); + $attribute->name = $property->name; + $attribute->type = (false !== strpos($propertyComment, '@var') && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) + ? $matches[1] + : 'mixed'; + + $this->collectAttributeTypeMetadata($metadata, $attribute); + + // checks if the property has @Enum + if (false !== strpos($propertyComment, '@Enum')) { + $context = 'property ' . $class->name . "::\$" . $property->name; + + self::$metadataParser->setTarget(Target::TARGET_PROPERTY); + + foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) { + if ( ! $annotation instanceof Enum) { + continue; + } + + $metadata['enum'][$property->name]['value'] = $annotation->value; + $metadata['enum'][$property->name]['literal'] = ( ! empty($annotation->literal)) + ? $annotation->literal + : $annotation->value; + } + } + } + + // choose the first property as default property + $metadata['default_property'] = reset($metadata['properties']); + } + } + + self::$annotationMetadata[$name] = $metadata; + } + + /** + * Collects parsing metadata for a given attribute. + * + * @param array $metadata + * @param Attribute $attribute + * + * @return void + */ + private function collectAttributeTypeMetadata(&$metadata, Attribute $attribute) + { + // handle internal type declaration + $type = isset(self::$typeMap[$attribute->type]) + ? self::$typeMap[$attribute->type] + : $attribute->type; + + // handle the case if the property type is mixed + if ('mixed' === $type) { + return; + } + + // Evaluate type + switch (true) { + // Checks if the property has array + case (false !== $pos = strpos($type, '<')): + $arrayType = substr($type, $pos + 1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; + break; + + // Checks if the property has type[] + case (false !== $pos = strrpos($type, '[')): + $arrayType = substr($type, 0, $pos); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; + break; + } + + $metadata['attribute_types'][$attribute->name]['type'] = $type; + $metadata['attribute_types'][$attribute->name]['value'] = $attribute->type; + $metadata['attribute_types'][$attribute->name]['required'] = $attribute->required; + } + + /** + * 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 MethodCall + * 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. + * + * @throws AnnotationException + */ + private function Annotation() + { + $this->match(DocLexer::T_AT); + + // check if we have an annotation + $name = $this->Identifier(); + + // 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)])) { + $found = true; + $name = (false !== $pos) + ? $this->imports[$loweredAlias] . substr($name, $pos) + : $this->imports[$loweredAlias]; + } elseif ( ! isset($this->ignoredAnnotationNames[$name]) + && isset($this->imports['__NAMESPACE__']) + && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name) + ) { + $name = $this->imports['__NAMESPACE__'].'\\'.$name; + $found = true; + } elseif (! isset($this->ignoredAnnotationNames[$name]) && $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 = $this->MethodCall(); + + if (isset(self::$annotationMetadata[$name]['enum'])) { + // checks all declared attributes + foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) { + // checks if the attribute is a valid enumerator + if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) { + throw AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]); + } + } + } + + // 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::attributeTypeError($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::attributeTypeError($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; + } + + /** + * MethodCall ::= ["(" [Values] ")"] + * + * @return array + */ + private function MethodCall() + { + $values = array(); + + if ( ! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { + return $values; + } + + $this->match(DocLexer::T_OPEN_PARENTHESIS); + + if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + $values = $this->Values(); + } + + $this->match(DocLexer::T_CLOSE_PARENTHESIS); + + return $values; + } + + /** + * Values ::= Array | Value {"," Value}* [","] + * + * @return array + */ + private function Values() + { + $values = array($this->Value()); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + break; + } + + $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; + } + + /** + * Constant ::= integer | string | float | boolean + * + * @return mixed + * + * @throws AnnotationException + */ + private function Constant() + { + $identifier = $this->Identifier(); + + if ( ! defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) { + list($className, $const) = explode('::', $identifier); + + $alias = (false === $pos = strpos($className, '\\')) ? $className : substr($className, 0, $pos); + $found = false; + + switch (true) { + case !empty ($this->namespaces): + foreach ($this->namespaces as $ns) { + if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) { + $className = $ns.'\\'.$className; + $found = true; + break; + } + } + break; + + case isset($this->imports[$loweredAlias = strtolower($alias)]): + $found = true; + $className = (false !== $pos) + ? $this->imports[$loweredAlias] . substr($className, $pos) + : $this->imports[$loweredAlias]; + break; + + default: + if(isset($this->imports['__NAMESPACE__'])) { + $ns = $this->imports['__NAMESPACE__']; + + if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) { + $className = $ns.'\\'.$className; + $found = true; + } + } + break; + } + + if ($found) { + $identifier = $className . '::' . $const; + } + } + + // checks if identifier ends with ::class, \strlen('::class') === 7 + $classPos = stripos($identifier, '::class'); + if ($classPos === strlen($identifier) - 7) { + return substr($identifier, 0, $classPos); + } + + if (!defined($identifier)) { + throw AnnotationException::semanticalErrorConstants($identifier, $this->context); + } + + return constant($identifier); + } + + /** + * Identifier ::= string + * + * @return string + */ + private function Identifier() + { + // check if we have an annotation + if ( ! $this->lexer->isNextTokenAny(self::$classIdentifiers)) { + $this->syntaxError('namespace separator or identifier'); + } + + $this->lexer->moveNext(); + + $className = $this->lexer->token['value']; + + 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); + + $className .= '\\' . $this->lexer->token['value']; + } + + return $className; + } + + /** + * 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(); + } + + if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { + return $this->Constant(); + } + + 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); + + // If the array is empty, stop parsing and return. + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { + $this->match(DocLexer::T_CLOSE_CURLY_BRACES); + + return $array; + } + + $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 | Constant + * Key ::= string | integer | Constant + * + * @return array + */ + private function ArrayEntry() + { + $peek = $this->lexer->glimpse(); + + if (DocLexer::T_EQUALS === $peek['type'] + || DocLexer::T_COLON === $peek['type']) { + + if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { + $key = $this->Constant(); + } else { + $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()); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php new file mode 100644 index 00000000000..24add1b3ba8 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php @@ -0,0 +1,288 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * File cache reader for annotations. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + * + * @deprecated the FileCacheReader is deprecated and will be removed + * in version 2.0.0 of doctrine/annotations. Please use the + * {@see \Doctrine\Common\Annotations\CachedReader} instead. + */ +class FileCacheReader implements Reader +{ + /** + * @var Reader + */ + private $reader; + + /** + * @var string + */ + private $dir; + + /** + * @var bool + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + /** + * @var array + */ + private $classNameHashes = array(); + + /** + * @var int + */ + private $umask; + + /** + * Constructor. + * + * @param Reader $reader + * @param string $cacheDir + * @param boolean $debug + * + * @throws \InvalidArgumentException + */ + public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002) + { + if ( ! is_int($umask)) { + throw new \InvalidArgumentException(sprintf( + 'The parameter umask must be an integer, was: %s', + gettype($umask) + )); + } + + $this->reader = $reader; + $this->umask = $umask; + + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777 & (~$this->umask), true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir)); + } + + $this->dir = rtrim($cacheDir, '\\/'); + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name]; + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($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; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name].'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($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; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name].'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($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; + } + + /** + * Saves the cache file. + * + * @param string $path + * @param mixed $data + * + * @return void + */ + private function saveCacheFile($path, $data) + { + if (!is_writable($this->dir)) { + 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.', $this->dir)); + } + + $tempfile = tempnam($this->dir, uniqid('', true)); + + if (false === $tempfile) { + throw new \RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir)); + } + + $written = file_put_contents($tempfile, 'umask)); + + if (false === rename($tempfile, $path)) { + @unlink($tempfile); + throw new \RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path)); + } + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Clears loaded annotations. + * + * @return void + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php new file mode 100644 index 00000000000..bf7fbdcdd3d --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Allows the reader to be used in-place of Doctrine's reader. + * + * @author Johannes M. Schmitt + */ +class IndexedReader implements Reader +{ + /** + * @var Reader + */ + private $delegate; + + /** + * Constructor. + * + * @param Reader $reader + */ + public function __construct(Reader $reader) + { + $this->delegate = $reader; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $annotations = array(); + foreach ($this->delegate->getClassAnnotations($class) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotation) + { + return $this->delegate->getClassAnnotation($class, $annotation); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $annotations = array(); + foreach ($this->delegate->getMethodAnnotations($method) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotation) + { + return $this->delegate->getMethodAnnotation($method, $annotation); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $annotations = array(); + foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotation) + { + return $this->delegate->getPropertyAnnotation($property, $annotation); + } + + /** + * Proxies all methods to the delegate. + * + * @param string $method + * @param array $args + * + * @return mixed + */ + public function __call($method, $args) + { + return call_user_func_array(array($this->delegate, $method), $args); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php new file mode 100644 index 00000000000..21ee7cc9030 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php @@ -0,0 +1,91 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use SplFileObject; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Fabien Potencier + * @author Christian Kaps + */ +final class PhpParser +{ + /** + * 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 (method_exists($class, 'getUseStatements')) { + return $class->getUseStatements(); + } + + if (false === $filename = $class->getFilename()) { + return array(); + } + + $content = $this->getFileContent($filename, $class->getStartLine()); + + if (null === $content) { + return array(); + } + + $namespace = preg_quote($class->getNamespaceName()); + $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content); + $tokenizer = new TokenParser('parseUseStatements($class->getNamespaceName()); + + return $statements; + } + + /** + * Gets the content of the file right up to the given line number. + * + * @param string $filename The name of the file to load. + * @param integer $lineNumber The number of lines to read from file. + * + * @return string The content of the file. + */ + private function getFileContent($filename, $lineNumber) + { + if ( ! is_file($filename)) { + return null; + } + + $content = ''; + $lineCnt = 0; + $file = new SplFileObject($filename); + while (!$file->eof()) { + if ($lineCnt++ == $lineNumber) { + break; + } + + $content .= $file->fgets(); + } + + return $content; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php new file mode 100644 index 00000000000..4774f87312b --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Interface for annotation readers. + * + * @author Johannes M. Schmitt + */ +interface Reader +{ + /** + * 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. + */ + function getClassAnnotations(\ReflectionClass $class); + + /** + * 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 object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getClassAnnotation(\ReflectionClass $class, $annotationName); + + /** + * Gets the annotations applied to a method. + * + * @param \ReflectionMethod $method The ReflectionMethod of the method from which + * the annotations should be read. + * + * @return array An array of Annotations. + */ + function getMethodAnnotations(\ReflectionMethod $method); + + /** + * Gets a method annotation. + * + * @param \ReflectionMethod $method The ReflectionMethod to read the annotations from. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getMethodAnnotation(\ReflectionMethod $method, $annotationName); + + /** + * 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. + */ + function getPropertyAnnotations(\ReflectionProperty $property); + + /** + * Gets a property annotation. + * + * @param \ReflectionProperty $property The ReflectionProperty to read the annotations from. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getPropertyAnnotation(\ReflectionProperty $property, $annotationName); +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php new file mode 100644 index 00000000000..d4757eea2fb --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php @@ -0,0 +1,127 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * 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 + * + * @return void + */ + public function addNamespace($namespace) + { + $this->parser->addNamespace($namespace); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + return $this->parser->parse($class->getDocComment(), 'class '.$class->getName()); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()'); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName()); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php new file mode 100644 index 00000000000..9bdcccec92d --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php @@ -0,0 +1,187 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Fabien Potencier + * @author Christian Kaps + */ +class TokenParser +{ + /** + * The token list. + * + * @var array + */ + private $tokens; + + /** + * The number of tokens. + * + * @var int + */ + private $numTokens; + + /** + * The current array pointer. + * + * @var int + */ + private $pointer = 0; + + /** + * @param string $contents + */ + public function __construct($contents) + { + $this->tokens = token_get_all($contents); + + // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it + // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored + // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a + // docblock. If the first thing in the file is a class without a doc block this would cause calls to + // getDocBlock() on said class to return our long lost doc_comment. Argh. + // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least + // it's harmless to us. + token_get_all("numTokens = count($this->tokens); + } + + /** + * Gets the next non whitespace and non comment token. + * + * @param boolean $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped. + * If FALSE then only whitespace and normal comments are skipped. + * + * @return array|null The token if exists, null otherwise. + */ + public function next($docCommentIsComment = TRUE) + { + for ($i = $this->pointer; $i < $this->numTokens; $i++) { + $this->pointer++; + if ($this->tokens[$i][0] === T_WHITESPACE || + $this->tokens[$i][0] === T_COMMENT || + ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) { + + continue; + } + + return $this->tokens[$i]; + } + + return null; + } + + /** + * Parses a single use statement. + * + * @return array A list with all found class names for a use statement. + */ + public 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; + } + + /** + * Gets all use statements. + * + * @param string $namespaceName The namespace name of the reflected class. + * + * @return array A list with all found use statements. + */ + public function parseUseStatements($namespaceName) + { + $statements = array(); + while (($token = $this->next())) { + if ($token[0] === T_USE) { + $statements = array_merge($statements, $this->parseUseStatement()); + continue; + } + 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; + } + + /** + * Gets the namespace. + * + * @return string The found namespace. + */ + public function parseNamespace() + { + $name = ''; + while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) { + $name .= $token[1]; + } + + return $name; + } + + /** + * Gets the class name. + * + * @return string The found class name. + */ + public function parseClass() + { + // Namespaces and class names are tokenized the same: T_STRINGs + // separated by T_NS_SEPARATOR so we can use one function to provide + // both. + return $this->parseNamespace(); + } +} diff --git a/vendor/doctrine/cache/.coveralls.yml b/vendor/doctrine/cache/.coveralls.yml new file mode 100644 index 00000000000..0c082336063 --- /dev/null +++ b/vendor/doctrine/cache/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: travis-ci +src_dir: lib +coverage_clover: build/logs/clover.xml diff --git a/vendor/doctrine/cache/.gitignore b/vendor/doctrine/cache/.gitignore new file mode 100644 index 00000000000..ca2302312b4 --- /dev/null +++ b/vendor/doctrine/cache/.gitignore @@ -0,0 +1,4 @@ +vendor/ +build/ +phpunit.xml +composer.lock \ No newline at end of file diff --git a/vendor/doctrine/cache/.travis.yml b/vendor/doctrine/cache/.travis.yml new file mode 100644 index 00000000000..a16fd9d930a --- /dev/null +++ b/vendor/doctrine/cache/.travis.yml @@ -0,0 +1,42 @@ +language: php + +sudo: false + +cache: + directories: + - vendor + - $HOME/.composer/cache + +php: + - 5.5 + - 5.6 + - 7.0 + - hhvm + +services: + - riak + - mongodb + - memcached + - redis-server + +before_install: + - if [[ $TRAVIS_PHP_VERSION != 'hhvm' ]] ; then pecl channel-update pecl.php.net; fi; + - if [[ $TRAVIS_PHP_VERSION != 'hhvm' && $TRAVIS_PHP_VERSION != '7.0' ]]; then pecl install riak-beta; fi; + - if [[ $TRAVIS_PHP_VERSION =~ 5.[56] ]] ; then echo yes | pecl install apcu-4.0.10; fi; + - if [[ $TRAVIS_PHP_VERSION = 7.* ]] ; then pecl config-set preferred_state beta; echo yes | pecl install apcu; fi; + - if [[ $TRAVIS_PHP_VERSION != 'hhvm' ]]; then phpenv config-add ./tests/travis/php.ini; fi; + +install: + - travis_retry composer install + +script: + - ./vendor/bin/phpunit -c ./tests/travis/phpunit.travis.xml -v + +after_script: + - php vendor/bin/coveralls -v + +matrix: + fast_finish: true + allow_failures: + - php: hhvm + - php: 7.0 diff --git a/vendor/doctrine/cache/LICENSE b/vendor/doctrine/cache/LICENSE new file mode 100644 index 00000000000..8c38cc1bc22 --- /dev/null +++ b/vendor/doctrine/cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 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/cache/README.md b/vendor/doctrine/cache/README.md new file mode 100644 index 00000000000..94f80a30ee9 --- /dev/null +++ b/vendor/doctrine/cache/README.md @@ -0,0 +1,14 @@ +# Doctrine Cache + +Master: [![Build Status](https://secure.travis-ci.org/doctrine/cache.png?branch=master)](http://travis-ci.org/doctrine/cache) [![Coverage Status](https://coveralls.io/repos/doctrine/cache/badge.png?branch=master)](https://coveralls.io/r/doctrine/cache?branch=master) + +[![Latest Stable Version](https://poser.pugx.org/doctrine/cache/v/stable.png)](https://packagist.org/packages/doctrine/cache) [![Total Downloads](https://poser.pugx.org/doctrine/cache/downloads.png)](https://packagist.org/packages/doctrine/cache) + +Cache component extracted from the Doctrine Common project. + +## Changelog + +### v1.2 + +* Added support for MongoDB as Cache Provider +* Fix namespace version reset diff --git a/vendor/doctrine/cache/UPGRADE.md b/vendor/doctrine/cache/UPGRADE.md new file mode 100644 index 00000000000..e1f8a503eef --- /dev/null +++ b/vendor/doctrine/cache/UPGRADE.md @@ -0,0 +1,16 @@ +# Upgrade to 1.4 + +## Minor BC Break: `Doctrine\Common\Cache\FileCache#$extension` is now `private`. + +If you need to override the value of `Doctrine\Common\Cache\FileCache#$extension`, then use the +second parameter of `Doctrine\Common\Cache\FileCache#__construct()` instead of overriding +the property in your own implementation. + +## Minor BC Break: file based caches paths changed + +`Doctrine\Common\Cache\FileCache`, `Doctrine\Common\Cache\PhpFileCache` and +`Doctrine\Common\Cache\FilesystemCache` are using a different cache paths structure. + +If you rely on warmed up caches for deployments, consider that caches generated +with `doctrine/cache` `<1.4` are not compatible with the new directory structure, +and will be ignored. diff --git a/vendor/doctrine/cache/build.properties b/vendor/doctrine/cache/build.properties new file mode 100644 index 00000000000..2d98c360aae --- /dev/null +++ b/vendor/doctrine/cache/build.properties @@ -0,0 +1,3 @@ +# Version class and file +project.version_class = Doctrine\\Common\\Cache\\Version +project.version_file = lib/Doctrine/Common/Cache/Version.php diff --git a/vendor/doctrine/cache/build.xml b/vendor/doctrine/cache/build.xml new file mode 100644 index 00000000000..a7c52e3cf3c --- /dev/null +++ b/vendor/doctrine/cache/build.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/cache/composer.json b/vendor/doctrine/cache/composer.json new file mode 100644 index 00000000000..7ef1727a107 --- /dev/null +++ b/vendor/doctrine/cache/composer.json @@ -0,0 +1,37 @@ +{ + "name": "doctrine/cache", + "type": "library", + "description": "Caching library offering an object-oriented API for many cache backends", + "keywords": ["cache", "caching"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "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.5|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "satooshi/php-coveralls": "~0.6", + "predis/predis": "~1.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "autoload": { + "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" } + }, + "autoload-dev": { + "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php new file mode 100644 index 00000000000..7c617f33da6 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * APC cache provider. + * + * @link www.doctrine-project.org + * @deprecated since version 1.6, use ApcuCache instead + * @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) + { + return apc_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return apc_store($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + // apc_delete returns false if the id does not exist + return apc_delete($id) || ! apc_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return apc_clear_cache() && apc_clear_cache('user'); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return apc_fetch($keys); + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + $result = apc_store($keysAndValues, null, $lifetime); + + return empty($result); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = apc_cache_info('', true); + $sma = apc_sma_info(); + + // @TODO - Temporary fix @see https://github.com/krakjoe/apcu/pull/42 + if (PHP_VERSION_ID >= 50500) { + $info['num_hits'] = isset($info['num_hits']) ? $info['num_hits'] : $info['nhits']; + $info['num_misses'] = isset($info['num_misses']) ? $info['num_misses'] : $info['nmisses']; + $info['start_time'] = isset($info['start_time']) ? $info['start_time'] : $info['stime']; + } + + 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_AVAILABLE => $sma['avail_mem'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php new file mode 100644 index 00000000000..d86e1be375d --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * APCu cache provider. + * + * @link www.doctrine-project.org + * @since 1.6 + * @author Kévin Dunglas + */ +class ApcuCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return apcu_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return apcu_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return apcu_store($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + // apcu_delete returns false if the id does not exist + return apcu_delete($id) || ! apcu_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return apcu_clear_cache(); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return apcu_fetch($keys); + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + $result = apcu_store($keysAndValues, null, $lifetime); + + return empty($result); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = apcu_cache_info(true); + $sma = apcu_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_AVAILABLE => $sma['avail_mem'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php new file mode 100644 index 00000000000..6610cc21735 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php @@ -0,0 +1,142 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Array cache driver. + * + * @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 each element being a tuple of [$data, $expiration], where the expiration is int|bool + */ + private $data = []; + + /** + * @var int + */ + private $hitsCount = 0; + + /** + * @var int + */ + private $missesCount = 0; + + /** + * @var int + */ + private $upTime; + + /** + * {@inheritdoc} + */ + public function __construct() + { + $this->upTime = time(); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + if (! $this->doContains($id)) { + $this->missesCount += 1; + + return false; + } + + $this->hitsCount += 1; + + return $this->data[$id][0]; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + if (! isset($this->data[$id])) { + return false; + } + + $expiration = $this->data[$id][1]; + + if ($expiration && $expiration < time()) { + $this->doDelete($id); + + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = [$data, $lifeTime ? time() + $lifeTime : false]; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->data = []; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return [ + Cache::STATS_HITS => $this->hitsCount, + Cache::STATS_MISSES => $this->missesCount, + Cache::STATS_UPTIME => $this->upTime, + Cache::STATS_MEMORY_USAGE => null, + Cache::STATS_MEMORY_AVAILABLE => null, + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php new file mode 100644 index 00000000000..89fe32307ff --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php @@ -0,0 +1,116 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + * @author Kévin Dunglas + */ +interface Cache +{ + const STATS_HITS = 'hits'; + const STATS_MISSES = 'misses'; + const STATS_UPTIME = 'uptime'; + const STATS_MEMORY_USAGE = 'memory_usage'; + const STATS_MEMORY_AVAILABLE = 'memory_available'; + /** + * Only for backward compatibility (may be removed in next major release) + * + * @deprecated + */ + const STATS_MEMORY_AVAILIABLE = 'memory_available'; + + /** + * Fetches an entry from the cache. + * + * @param string $id The id of the cache entry to fetch. + * + * @return mixed The cached data or FALSE, if no cache entry exists for the given id. + */ + public function fetch($id); + + /** + * Tests if an entry exists in the cache. + * + * @param string $id The cache id of the entry to check for. + * + * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + public function contains($id); + + /** + * Puts data into the cache. + * + * If a cache entry with the given id already exists, its data will be replaced. + * + * @param string $id The cache id. + * @param mixed $data The cache entry/data. + * @param int $lifeTime The lifetime in number of seconds for this cache entry. + * If zero (the default), the entry never expires (although it may be deleted from the cache + * to make place for other entries). + * + * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + public function save($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id The cache id. + * + * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise. + * Deleting a non-existing entry is considered successful. + */ + public function delete($id); + + /** + * Retrieves cached information from the 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 + * + * @return array|null An associative array with server's statistics if available, NULL otherwise. + */ + public function getStats(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php new file mode 100644 index 00000000000..9f579237a61 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php @@ -0,0 +1,312 @@ +. + */ + +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, FlushableCache, ClearableCache, MultiGetCache, MultiPutCache +{ + const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; + + /** + * The namespace to prefix all cache ids with. + * + * @var string + */ + private $namespace = ''; + + /** + * The namespace version. + * + * @var integer|null + */ + private $namespaceVersion; + + /** + * Sets the namespace to prefix all cache ids with. + * + * @param string $namespace + * + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + $this->namespaceVersion = null; + } + + /** + * Retrieves 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 fetchMultiple(array $keys) + { + if (empty($keys)) { + return array(); + } + + // note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys + $namespacedKeys = array_combine($keys, array_map(array($this, 'getNamespacedId'), $keys)); + $items = $this->doFetchMultiple($namespacedKeys); + $foundItems = array(); + + // no internal array function supports this sort of mapping: needs to be iterative + // this filters and combines keys in one pass + foreach ($namespacedKeys as $requestedKey => $namespacedKey) { + if (isset($items[$namespacedKey]) || array_key_exists($namespacedKey, $items)) { + $foundItems[$requestedKey] = $items[$namespacedKey]; + } + } + + return $foundItems; + } + + /** + * {@inheritdoc} + */ + public function saveMultiple(array $keysAndValues, $lifetime = 0) + { + $namespacedKeysAndValues = array(); + foreach ($keysAndValues as $key => $value) { + $namespacedKeysAndValues[$this->getNamespacedId($key)] = $value; + } + + return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime); + } + + /** + * {@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(); + } + + /** + * {@inheritDoc} + */ + public function flushAll() + { + return $this->doFlush(); + } + + /** + * {@inheritDoc} + */ + public function deleteAll() + { + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $namespaceVersion = $this->getNamespaceVersion() + 1; + + if ($this->doSave($namespaceCacheKey, $namespaceVersion)) { + $this->namespaceVersion = $namespaceVersion; + + return true; + } + + return false; + } + + /** + * Prefixes the passed id with the configured namespace value. + * + * @param string $id The id to namespace. + * + * @return string The namespaced id. + */ + private function getNamespacedId($id) + { + $namespaceVersion = $this->getNamespaceVersion(); + + return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + } + + /** + * Returns the namespace cache key. + * + * @return string + */ + private function getNamespaceCacheKey() + { + return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + } + + /** + * Returns the namespace version. + * + * @return integer + */ + private function getNamespaceVersion() + { + if (null !== $this->namespaceVersion) { + return $this->namespaceVersion; + } + + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $this->namespaceVersion = $this->doFetch($namespaceCacheKey) ?: 1; + + return $this->namespaceVersion; + } + + /** + * Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it. + * + * @param array $keys Array of keys to retrieve from cache + * @return array Array of values retrieved for the given keys. + */ + protected function doFetchMultiple(array $keys) + { + $returnValues = array(); + + foreach ($keys as $key) { + if (false !== ($item = $this->doFetch($key)) || $this->doContains($key)) { + $returnValues[$key] = $item; + } + } + + return $returnValues; + } + + /** + * Fetches an entry from the cache. + * + * @param string $id The id of the cache entry to fetch. + * + * @return mixed|false The cached data or FALSE, if no cache entry exists for the given id. + */ + abstract protected function doFetch($id); + + /** + * Tests if an entry exists in the cache. + * + * @param string $id The cache id of the entry to check for. + * + * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + abstract protected function doContains($id); + + /** + * Default implementation of doSaveMultiple. Each driver that supports multi-put should override it. + * + * @param array $keysAndValues Array of keys and values to save in cache + * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these + * cache entries (0 => infinite lifeTime). + * + * @return bool TRUE if the operation was successful, FALSE if it wasn't. + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + $success = true; + + foreach ($keysAndValues as $key => $value) { + if (!$this->doSave($key, $value, $lifetime)) { + $success = false; + } + } + + return $success; + } + + /** + * 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 bool TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + abstract protected function doSave($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id The cache id. + * + * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doDelete($id); + + /** + * Flushes all cache entries. + * + * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + abstract protected function doFlush(); + + /** + * Retrieves cached information from the data store. + * + * @since 2.2 + * + * @return array|null An associative array with server's statistics if available, NULL otherwise. + */ + abstract protected function doGetStats(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php new file mode 100644 index 00000000000..96c9b5479fb --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php @@ -0,0 +1,147 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Cache provider that allows to easily chain multiple cache providers + * + * @author Michaël Gallego + */ +class ChainCache extends CacheProvider +{ + /** + * @var CacheProvider[] + */ + private $cacheProviders = array(); + + /** + * Constructor + * + * @param CacheProvider[] $cacheProviders + */ + public function __construct($cacheProviders = array()) + { + $this->cacheProviders = $cacheProviders; + } + + /** + * {@inheritDoc} + */ + public function setNamespace($namespace) + { + parent::setNamespace($namespace); + + foreach ($this->cacheProviders as $cacheProvider) { + $cacheProvider->setNamespace($namespace); + } + } + + /** + * {@inheritDoc} + */ + protected function doFetch($id) + { + foreach ($this->cacheProviders as $key => $cacheProvider) { + if ($cacheProvider->doContains($id)) { + $value = $cacheProvider->doFetch($id); + + // We populate all the previous cache layers (that are assumed to be faster) + for ($subKey = $key - 1 ; $subKey >= 0 ; $subKey--) { + $this->cacheProviders[$subKey]->doSave($id, $value); + } + + return $value; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function doContains($id) + { + foreach ($this->cacheProviders as $cacheProvider) { + if ($cacheProvider->doContains($id)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $stored = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $stored = $cacheProvider->doSave($id, $data, $lifeTime) && $stored; + } + + return $stored; + } + + /** + * {@inheritDoc} + */ + protected function doDelete($id) + { + $deleted = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $deleted = $cacheProvider->doDelete($id) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritDoc} + */ + protected function doFlush() + { + $flushed = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $flushed = $cacheProvider->doFlush() && $flushed; + } + + return $flushed; + } + + /** + * {@inheritDoc} + */ + protected function doGetStats() + { + // We return all the stats from all adapters + $stats = array(); + + foreach ($this->cacheProviders as $cacheProvider) { + $stats[] = $cacheProvider->doGetStats(); + } + + return $stats; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php new file mode 100644 index 00000000000..3a91eaf3a27 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache that can be flushed. + * + * Intended to be used for partial clearing of a cache namespace. For a more + * global "flushing", see {@see FlushableCache}. + * + * @link www.doctrine-project.org + * @since 1.4 + * @author Adirelle + */ +interface ClearableCache +{ + /** + * Deletes all cache entries in the current cache namespace. + * + * @return bool TRUE if the cache entries were successfully deleted, FALSE otherwise. + */ + public function deleteAll(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php new file mode 100644 index 00000000000..c21691df962 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Couchbase; + +/** + * Couchbase cache provider. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Michael Nitschinger + */ +class CouchbaseCache extends CacheProvider +{ + /** + * @var Couchbase|null + */ + private $couchbase; + + /** + * Sets the Couchbase instance to use. + * + * @param Couchbase $couchbase + * + * @return void + */ + public function setCouchbase(Couchbase $couchbase) + { + $this->couchbase = $couchbase; + } + + /** + * Gets the Couchbase instance used by the cache. + * + * @return Couchbase|null + */ + public function getCouchbase() + { + return $this->couchbase; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->couchbase->get($id) ?: false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (null !== $this->couchbase->get($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->couchbase->set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->couchbase->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->couchbase->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->couchbase->getStats(); + $servers = $this->couchbase->getServers(); + $server = explode(":", $servers[0]); + $key = $server[0] . ":" . "11210"; + $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_AVAILABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php new file mode 100644 index 00000000000..b2e0427e59a --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php @@ -0,0 +1,286 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + * @author Tobias Schultze + */ +abstract class FileCache extends CacheProvider +{ + /** + * The cache directory. + * + * @var string + */ + protected $directory; + + /** + * The cache file extension. + * + * @var string + */ + private $extension; + + /** + * @var int + */ + private $umask; + + /** + * @var int + */ + private $directoryStringLength; + + /** + * @var int + */ + private $extensionStringLength; + + /** + * @var bool + */ + private $isRunningOnWindows; + + /** + * Constructor. + * + * @param string $directory The cache directory. + * @param string $extension The cache file extension. + * + * @throws \InvalidArgumentException + */ + public function __construct($directory, $extension = '', $umask = 0002) + { + // YES, this needs to be *before* createPathIfNeeded() + if ( ! is_int($umask)) { + throw new \InvalidArgumentException(sprintf( + 'The umask parameter is required to be integer, was: %s', + gettype($umask) + )); + } + $this->umask = $umask; + + if ( ! $this->createPathIfNeeded($directory)) { + throw new \InvalidArgumentException(sprintf( + 'The directory "%s" does not exist and could not be created.', + $directory + )); + } + + if ( ! is_writable($directory)) { + throw new \InvalidArgumentException(sprintf( + 'The directory "%s" is not writable.', + $directory + )); + } + + // YES, this needs to be *after* createPathIfNeeded() + $this->directory = realpath($directory); + $this->extension = (string) $extension; + + $this->directoryStringLength = strlen($this->directory); + $this->extensionStringLength = strlen($this->extension); + $this->isRunningOnWindows = defined('PHP_WINDOWS_VERSION_BUILD'); + } + + /** + * Gets the cache directory. + * + * @return string + */ + public function getDirectory() + { + return $this->directory; + } + + /** + * Gets the cache file extension. + * + * @return string + */ + public function getExtension() + { + return $this->extension; + } + + /** + * @param string $id + * + * @return string + */ + protected function getFilename($id) + { + $hash = hash('sha256', $id); + + // This ensures that the filename is unique and that there are no invalid chars in it. + if ( + '' === $id + || ((strlen($id) * 2 + $this->extensionStringLength) > 255) + || ($this->isRunningOnWindows && ($this->directoryStringLength + 4 + strlen($id) * 2 + $this->extensionStringLength) > 258) + ) { + // Most filesystems have a limit of 255 chars for each path component. On Windows the the whole path is limited + // to 260 chars (including terminating null char). Using long UNC ("\\?\" prefix) does not work with the PHP API. + // And there is a bug in PHP (https://bugs.php.net/bug.php?id=70943) with path lengths of 259. + // So if the id in hex representation would surpass the limit, we use the hash instead. The prefix prevents + // collisions between the hash and bin2hex. + $filename = '_' . $hash; + } else { + $filename = bin2hex($id); + } + + return $this->directory + . DIRECTORY_SEPARATOR + . substr($hash, 0, 2) + . DIRECTORY_SEPARATOR + . $filename + . $this->extension; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + $filename = $this->getFilename($id); + + return @unlink($filename) || ! file_exists($filename); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + foreach ($this->getIterator() as $name => $file) { + if ($file->isDir()) { + // Remove the intermediate directories which have been created to balance the tree. It only takes effect + // if the directory is empty. If several caches share the same directory but with different file extensions, + // the other ones are not removed. + @rmdir($name); + } elseif ($this->isFilenameEndingWithExtension($name)) { + // If an extension is set, only remove files which end with the given extension. + // If no extension is set, we have no other choice than removing everything. + @unlink($name); + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $usage = 0; + foreach ($this->getIterator() as $name => $file) { + if (! $file->isDir() && $this->isFilenameEndingWithExtension($name)) { + $usage += $file->getSize(); + } + } + + $free = disk_free_space($this->directory); + + return array( + Cache::STATS_HITS => null, + Cache::STATS_MISSES => null, + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $usage, + Cache::STATS_MEMORY_AVAILABLE => $free, + ); + } + + /** + * Create path if needed. + * + * @param string $path + * @return bool TRUE on success or if path already exists, FALSE if path cannot be created. + */ + private function createPathIfNeeded($path) + { + if ( ! is_dir($path)) { + if (false === @mkdir($path, 0777 & (~$this->umask), true) && !is_dir($path)) { + return false; + } + } + + return true; + } + + /** + * Writes a string content to file in an atomic way. + * + * @param string $filename Path to the file where to write the data. + * @param string $content The content to write + * + * @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error. + */ + protected function writeFile($filename, $content) + { + $filepath = pathinfo($filename, PATHINFO_DIRNAME); + + if ( ! $this->createPathIfNeeded($filepath)) { + return false; + } + + if ( ! is_writable($filepath)) { + return false; + } + + $tmpFile = tempnam($filepath, 'swap'); + @chmod($tmpFile, 0666 & (~$this->umask)); + + if (file_put_contents($tmpFile, $content) !== false) { + if (@rename($tmpFile, $filename)) { + return true; + } + + @unlink($tmpFile); + } + + return false; + } + + /** + * @return \Iterator + */ + private function getIterator() + { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + } + + /** + * @param string $name The filename + * + * @return bool + */ + private function isFilenameEndingWithExtension($name) + { + return '' === $this->extension + || strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php new file mode 100644 index 00000000000..d988294f6fb --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Filesystem cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class FilesystemCache extends FileCache +{ + const EXTENSION = '.doctrinecache.data'; + + /** + * {@inheritdoc} + */ + public function __construct($directory, $extension = self::EXTENSION, $umask = 0002) + { + parent::__construct($directory, $extension, $umask); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $data = ''; + $lifetime = -1; + $filename = $this->getFilename($id); + + if ( ! is_file($filename)) { + return false; + } + + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (int) $line; + } + + if ($lifetime !== 0 && $lifetime < time()) { + fclose($resource); + + return false; + } + + while (false !== ($line = fgets($resource))) { + $data .= $line; + } + + fclose($resource); + + return unserialize($data); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $lifetime = -1; + $filename = $this->getFilename($id); + + if ( ! is_file($filename)) { + return false; + } + + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (int) $line; + } + + fclose($resource); + + return $lifetime === 0 || $lifetime > time(); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + $lifeTime = time() + $lifeTime; + } + + $data = serialize($data); + $filename = $this->getFilename($id); + + return $this->writeFile($filename, $lifeTime . PHP_EOL . $data); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php new file mode 100644 index 00000000000..4311d4f5939 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache that can be flushed. + * + * @link www.doctrine-project.org + * @since 1.4 + * @author Adirelle + */ +interface FlushableCache +{ + /** + * Flushes all cache entries, globally. + * + * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + public function flushAll(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php new file mode 100644 index 00000000000..8afaeeacc2b --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcache; + +/** + * Memcache cache provider. + * + * @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|null + */ + private $memcache; + + /** + * Sets the memcache instance to use. + * + * @param Memcache $memcache + * + * @return void + */ + public function setMemcache(Memcache $memcache) + { + $this->memcache = $memcache; + } + + /** + * Gets the memcache instance used by the cache. + * + * @return Memcache|null + */ + public function getMemcache() + { + return $this->memcache; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $flags = null; + $this->memcache->get($id, $flags); + + //if memcache has changed the value of "flags", it means the value exists + return ($flags !== null); + } + + /** + * {@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) + { + // Memcache::delete() returns false if entry does not exist + return $this->memcache->delete($id) || ! $this->doContains($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_AVAILABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php new file mode 100644 index 00000000000..408e452b904 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcached; + +/** + * Memcached cache provider. + * + * @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|null + */ + private $memcached; + + /** + * Sets the memcache instance to use. + * + * @param Memcached $memcached + * + * @return void + */ + public function setMemcached(Memcached $memcached) + { + $this->memcached = $memcached; + } + + /** + * Gets the memcached instance used by the cache. + * + * @return Memcached|null + */ + public function getMemcached() + { + return $this->memcached; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcached->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return $this->memcached->getMulti($keys); + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + if ($lifetime > 30 * 24 * 3600) { + $lifetime = time() + $lifetime; + } + + return $this->memcached->setMulti($keysAndValues, null, $lifetime); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return false !== $this->memcached->get($id) + || $this->memcached->getResultCode() !== Memcached::RES_NOTFOUND; + } + + /** + * {@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) + || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND; + } + + /** + * {@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_AVAILABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php new file mode 100644 index 00000000000..75fe0ca1130 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php @@ -0,0 +1,197 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use MongoBinData; +use MongoCollection; +use MongoCursorException; +use MongoDate; + +/** + * MongoDB cache provider. + * + * @since 1.1 + * @author Jeremy Mikola + */ +class MongoDBCache extends CacheProvider +{ + /** + * The data field will store the serialized PHP value. + */ + const DATA_FIELD = 'd'; + + /** + * The expiration field will store a MongoDate value indicating when the + * cache entry should expire. + * + * With MongoDB 2.2+, entries can be automatically deleted by MongoDB by + * indexing this field with the "expireAfterSeconds" option equal to zero. + * This will direct MongoDB to regularly query for and delete any entries + * whose date is older than the current time. Entries without a date value + * in this field will be ignored. + * + * The cache provider will also check dates on its own, in case expired + * entries are fetched before MongoDB's TTLMonitor pass can expire them. + * + * @see http://docs.mongodb.org/manual/tutorial/expire-data/ + */ + const EXPIRATION_FIELD = 'e'; + + /** + * @var MongoCollection + */ + private $collection; + + /** + * Constructor. + * + * This provider will default to the write concern and read preference + * options set on the MongoCollection instance (or inherited from MongoDB or + * MongoClient). Using an unacknowledged write concern (< 1) may make the + * return values of delete() and save() unreliable. Reading from secondaries + * may make contain() and fetch() unreliable. + * + * @see http://www.php.net/manual/en/mongo.readpreferences.php + * @see http://www.php.net/manual/en/mongo.writeconcerns.php + * @param MongoCollection $collection + */ + public function __construct(MongoCollection $collection) + { + $this->collection = $collection; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $document = $this->collection->findOne(array('_id' => $id), array(self::DATA_FIELD, self::EXPIRATION_FIELD)); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->doDelete($id); + return false; + } + + return unserialize($document[self::DATA_FIELD]->bin); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $document = $this->collection->findOne(array('_id' => $id), array(self::EXPIRATION_FIELD)); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->doDelete($id); + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + try { + $result = $this->collection->update( + array('_id' => $id), + array('$set' => array( + self::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null), + self::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY), + )), + array('upsert' => true, 'multiple' => false) + ); + } catch (MongoCursorException $e) { + return false; + } + + return isset($result['ok']) ? $result['ok'] == 1 : true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + $result = $this->collection->remove(array('_id' => $id)); + + return isset($result['ok']) ? $result['ok'] == 1 : true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + // Use remove() in lieu of drop() to maintain any collection indexes + $result = $this->collection->remove(); + + return isset($result['ok']) ? $result['ok'] == 1 : true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $serverStatus = $this->collection->db->command(array( + 'serverStatus' => 1, + 'locks' => 0, + 'metrics' => 0, + 'recordStats' => 0, + 'repl' => 0, + )); + + $collStats = $this->collection->db->command(array('collStats' => 1)); + + return array( + Cache::STATS_HITS => null, + Cache::STATS_MISSES => null, + Cache::STATS_UPTIME => (isset($serverStatus['uptime']) ? (int) $serverStatus['uptime'] : null), + Cache::STATS_MEMORY_USAGE => (isset($collStats['size']) ? (int) $collStats['size'] : null), + Cache::STATS_MEMORY_AVAILABLE => null, + ); + } + + /** + * Check if the document is expired. + * + * @param array $document + * + * @return bool + */ + private function isExpired(array $document) + { + return isset($document[self::EXPIRATION_FIELD]) && + $document[self::EXPIRATION_FIELD] instanceof MongoDate && + $document[self::EXPIRATION_FIELD]->sec < time(); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php new file mode 100644 index 00000000000..df7146d78ef --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers that allows to get many items at once. + * + * @link www.doctrine-project.org + * @since 1.4 + * @author Asmir Mustafic + */ +interface MultiGetCache +{ + /** + * Returns an associative array of values for keys is found in cache. + * + * @param string[] $keys Array of keys to retrieve from cache + * @return mixed[] Array of retrieved values, indexed by the specified keys. + * Values that couldn't be retrieved are not contained in this array. + */ + function fetchMultiple(array $keys); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php new file mode 100644 index 00000000000..bf87ea9f1cd --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers that allows to put many items at once. + * + * @link www.doctrine-project.org + * @since 1.6 + * @author Daniel Gorgan + */ +interface MultiPutCache +{ + /** + * Returns a boolean value indicating if the operation succeeded. + * + * @param array $keysAndValues Array of keys and values to save in cache + * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these + * cache entries (0 => infinite lifeTime). + * + * @return bool TRUE if the operation was successful, FALSE if it wasn't. + */ + function saveMultiple(array $keysAndValues, $lifetime = 0); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php new file mode 100644 index 00000000000..5e7519674e3 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Php file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class PhpFileCache extends FileCache +{ + const EXTENSION = '.doctrinecache.php'; + + /** + * {@inheritdoc} + */ + public function __construct($directory, $extension = self::EXTENSION, $umask = 0002) + { + parent::__construct($directory, $extension, $umask); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $value = $this->includeFileForId($id); + + if (! $value) { + return false; + } + + if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) { + return false; + } + + return $value['data']; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $value = $this->includeFileForId($id); + + if (! $value) { + return false; + } + + return $value['lifetime'] === 0 || $value['lifetime'] > time(); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + $lifeTime = time() + $lifeTime; + } + + if (is_object($data) && ! method_exists($data, '__set_state')) { + throw new \InvalidArgumentException( + "Invalid argument given, PhpFileCache only allows objects that implement __set_state() " . + "and fully support var_export(). You can use the FilesystemCache to save arbitrary object " . + "graphs using serialize()/deserialize()." + ); + } + + $filename = $this->getFilename($id); + + $value = array( + 'lifetime' => $lifeTime, + 'data' => $data + ); + + $value = var_export($value, true); + $code = sprintf('writeFile($filename, $code); + } + + /** + * @param string $id + * + * @return array|false + */ + private function includeFileForId($id) + { + $fileName = $this->getFilename($id); + + // note: error suppression is still faster than `file_exists`, `is_file` and `is_readable` + $value = @include $fileName; + + if (! isset($value['lifetime'])) { + return false; + } + + return $value; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php new file mode 100644 index 00000000000..980e2660603 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php @@ -0,0 +1,136 @@ + + */ +class PredisCache extends CacheProvider +{ + /** + * @var ClientInterface + */ + private $client; + + /** + * @param ClientInterface $client + * + * @return void + */ + public function __construct(ClientInterface $client) + { + $this->client = $client; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $result = $this->client->get($id); + if (null === $result) { + return false; + } + + return unserialize($result); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + $fetchedItems = call_user_func_array(array($this->client, 'mget'), $keys); + + return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems))); + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + if ($lifetime) { + $success = true; + + // Keys have lifetime, use SETEX for each of them + foreach ($keysAndValues as $key => $value) { + $response = $this->client->setex($key, $lifetime, serialize($value)); + + if ((string) $response != 'OK') { + $success = false; + } + } + + return $success; + } + + // No lifetime, use MSET + $response = $this->client->mset(array_map(function ($value) { + return serialize($value); + }, $keysAndValues)); + + return (string) $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->client->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $data = serialize($data); + if ($lifeTime > 0) { + $response = $this->client->setex($id, $lifeTime, $data); + } else { + $response = $this->client->set($id, $data); + } + + return $response === true || $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->client->del($id) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $response = $this->client->flushdb(); + + return $response === true || $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->client->info(); + + return array( + Cache::STATS_HITS => $info['Stats']['keyspace_hits'], + Cache::STATS_MISSES => $info['Stats']['keyspace_misses'], + Cache::STATS_UPTIME => $info['Server']['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['Memory']['used_memory'], + Cache::STATS_MEMORY_AVAILABLE => false + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php new file mode 100644 index 00000000000..a4f0e6e416c --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php @@ -0,0 +1,175 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use Redis; + +/** + * Redis cache provider. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Osman Ungur + */ +class RedisCache extends CacheProvider +{ + /** + * @var Redis|null + */ + private $redis; + + /** + * Sets the redis instance to use. + * + * @param Redis $redis + * + * @return void + */ + public function setRedis(Redis $redis) + { + $redis->setOption(Redis::OPT_SERIALIZER, $this->getSerializerValue()); + $this->redis = $redis; + } + + /** + * Gets the redis instance used by the cache. + * + * @return Redis|null + */ + public function getRedis() + { + return $this->redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->redis->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + $fetchedItems = array_combine($keys, $this->redis->mget($keys)); + + // Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data. + $foundItems = array(); + + foreach ($fetchedItems as $key => $value) { + if (false !== $value || $this->redis->exists($key)) { + $foundItems[$key] = $value; + } + } + + return $foundItems; + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + if ($lifetime) { + $success = true; + + // Keys have lifetime, use SETEX for each of them + foreach ($keysAndValues as $key => $value) { + if (!$this->redis->setex($key, $lifetime, $value)) { + $success = false; + } + } + + return $success; + } + + // No lifetime, use MSET + return (bool) $this->redis->mset($keysAndValues); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + return $this->redis->setex($id, $lifeTime, $data); + } + + return $this->redis->set($id, $data); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->redis->delete($id) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->redis->flushDB(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->redis->info(); + return array( + Cache::STATS_HITS => $info['keyspace_hits'], + Cache::STATS_MISSES => $info['keyspace_misses'], + Cache::STATS_UPTIME => $info['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['used_memory'], + Cache::STATS_MEMORY_AVAILABLE => false + ); + } + + /** + * Returns the serializer constant to use. If Redis is compiled with + * igbinary support, that is used. Otherwise the default PHP serializer is + * used. + * + * @return integer One of the Redis::SERIALIZER_* constants + */ + protected function getSerializerValue() + { + if (defined('HHVM_VERSION')) { + return Redis::SERIALIZER_PHP; + } + return defined('Redis::SERIALIZER_IGBINARY') ? Redis::SERIALIZER_IGBINARY : Redis::SERIALIZER_PHP; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php new file mode 100644 index 00000000000..0baa3f2531c --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php @@ -0,0 +1,250 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use Riak\Bucket; +use Riak\Connection; +use Riak\Input; +use Riak\Exception; +use Riak\Object; + +/** + * Riak cache provider. + * + * @link www.doctrine-project.org + * @since 1.1 + * @author Guilherme Blanco + */ +class RiakCache extends CacheProvider +{ + const EXPIRES_HEADER = 'X-Riak-Meta-Expires'; + + /** + * @var \Riak\Bucket + */ + private $bucket; + + /** + * Sets the riak bucket instance to use. + * + * @param \Riak\Bucket $bucket + */ + public function __construct(Bucket $bucket) + { + $this->bucket = $bucket; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + try { + $response = $this->bucket->get($id); + + // No objects found + if ( ! $response->hasObject()) { + return false; + } + + // Check for attempted siblings + $object = ($response->hasSiblings()) + ? $this->resolveConflict($id, $response->getVClock(), $response->getObjectList()) + : $response->getFirstObject(); + + // Check for expired object + if ($this->isExpired($object)) { + $this->bucket->delete($object); + + return false; + } + + return unserialize($object->getContent()); + } catch (Exception\RiakException $e) { + // Covers: + // - Riak\ConnectionException + // - Riak\CommunicationException + // - Riak\UnexpectedResponseException + // - Riak\NotFoundException + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + try { + // We only need the HEAD, not the entire object + $input = new Input\GetInput(); + + $input->setReturnHead(true); + + $response = $this->bucket->get($id, $input); + + // No objects found + if ( ! $response->hasObject()) { + return false; + } + + $object = $response->getFirstObject(); + + // Check for expired object + if ($this->isExpired($object)) { + $this->bucket->delete($object); + + return false; + } + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + try { + $object = new Object($id); + + $object->setContent(serialize($data)); + + if ($lifeTime > 0) { + $object->addMetadata(self::EXPIRES_HEADER, (string) (time() + $lifeTime)); + } + + $this->bucket->put($object); + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + try { + $this->bucket->delete($id); + + return true; + } catch (Exception\BadArgumentsException $e) { + // Key did not exist on cluster already + } catch (Exception\RiakException $e) { + // Covers: + // - Riak\Exception\ConnectionException + // - Riak\Exception\CommunicationException + // - Riak\Exception\UnexpectedResponseException + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + try { + $keyList = $this->bucket->getKeyList(); + + foreach ($keyList as $key) { + $this->bucket->delete($key); + } + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + // Only exposed through HTTP stats API, not Protocol Buffers API + return null; + } + + /** + * Check if a given Riak Object have expired. + * + * @param \Riak\Object $object + * + * @return bool + */ + private function isExpired(Object $object) + { + $metadataMap = $object->getMetadataMap(); + + return isset($metadataMap[self::EXPIRES_HEADER]) + && $metadataMap[self::EXPIRES_HEADER] < time(); + } + + /** + * On-read conflict resolution. Applied approach here is last write wins. + * Specific needs may override this method to apply alternate conflict resolutions. + * + * {@internal Riak does not attempt to resolve a write conflict, and store + * it as sibling of conflicted one. By following this approach, it is up to + * the next read to resolve the conflict. When this happens, your fetched + * object will have a list of siblings (read as a list of objects). + * In our specific case, we do not care about the intermediate ones since + * they are all the same read from storage, and we do apply a last sibling + * (last write) wins logic. + * If by any means our resolution generates another conflict, it'll up to + * next read to properly solve it.} + * + * @param string $id + * @param string $vClock + * @param array $objectList + * + * @return \Riak\Object + */ + protected function resolveConflict($id, $vClock, array $objectList) + { + // Our approach here is last-write wins + $winner = $objectList[count($objectList)]; + + $putInput = new Input\PutInput(); + $putInput->setVClock($vClock); + + $mergedObject = new Object($id); + $mergedObject->setContent($winner->getContent()); + + $this->bucket->put($mergedObject, $putInput); + + return $mergedObject; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php new file mode 100644 index 00000000000..0bf6e4d44af --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php @@ -0,0 +1,220 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use SQLite3; +use SQLite3Result; + +/** + * SQLite3 cache provider. + * + * @since 1.4 + * @author Jake Bell + */ +class SQLite3Cache extends CacheProvider +{ + /** + * The ID field will store the cache key. + */ + const ID_FIELD = 'k'; + + /** + * The data field will store the serialized PHP value. + */ + const DATA_FIELD = 'd'; + + /** + * The expiration field will store a date value indicating when the + * cache entry should expire. + */ + const EXPIRATION_FIELD = 'e'; + + /** + * @var SQLite3 + */ + private $sqlite; + + /** + * @var string + */ + private $table; + + /** + * Constructor. + * + * Calling the constructor will ensure that the database file and table + * exist and will create both if they don't. + * + * @param SQLite3 $sqlite + * @param string $table + */ + public function __construct(SQLite3 $sqlite, $table) + { + $this->sqlite = $sqlite; + $this->table = (string) $table; + + list($id, $data, $exp) = $this->getFields(); + + return $this->sqlite->exec(sprintf( + 'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)', + $table, + $id, + $data, + $exp + )); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + if ($item = $this->findById($id)) { + return unserialize($item[self::DATA_FIELD]); + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return null !== $this->findById($id, false); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $statement = $this->sqlite->prepare(sprintf( + 'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)', + $this->table, + implode(',', $this->getFields()) + )); + + $statement->bindValue(':id', $id); + $statement->bindValue(':data', serialize($data), SQLITE3_BLOB); + $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null); + + return $statement->execute() instanceof SQLite3Result; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + list($idField) = $this->getFields(); + + $statement = $this->sqlite->prepare(sprintf( + 'DELETE FROM %s WHERE %s = :id', + $this->table, + $idField + )); + + $statement->bindValue(':id', $id); + + return $statement->execute() instanceof SQLite3Result; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table)); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + // no-op. + } + + /** + * Find a single row by ID. + * + * @param mixed $id + * @param bool $includeData + * + * @return array|null + */ + private function findById($id, $includeData = true) + { + list($idField) = $fields = $this->getFields(); + + if (!$includeData) { + $key = array_search(static::DATA_FIELD, $fields); + unset($fields[$key]); + } + + $statement = $this->sqlite->prepare(sprintf( + 'SELECT %s FROM %s WHERE %s = :id LIMIT 1', + implode(',', $fields), + $this->table, + $idField + )); + + $statement->bindValue(':id', $id, SQLITE3_TEXT); + + $item = $statement->execute()->fetchArray(SQLITE3_ASSOC); + + if ($item === false) { + return null; + } + + if ($this->isExpired($item)) { + $this->doDelete($id); + + return null; + } + + return $item; + } + + /** + * Gets an array of the fields in our table. + * + * @return array + */ + private function getFields() + { + return array(static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD); + } + + /** + * Check if the item is expired. + * + * @param array $item + * + * @return bool + */ + private function isExpired(array $item) + { + return isset($item[static::EXPIRATION_FIELD]) && + $item[self::EXPIRATION_FIELD] !== null && + $item[self::EXPIRATION_FIELD] < time(); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php new file mode 100644 index 00000000000..eff259ae382 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php @@ -0,0 +1,25 @@ +. + */ + +namespace Doctrine\Common\Cache; + +class Version +{ + const VERSION = '1.6.0'; +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php new file mode 100644 index 00000000000..65e8456faad --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Void cache driver. The cache could be of use in tests where you don`t need to cache anything. + * + * @link www.doctrine-project.org + * @since 1.5 + * @author Kotlyar Maksim + */ +class VoidCache extends CacheProvider +{ + /** + * {@inheritDoc} + */ + protected function doFetch($id) + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function doContains($id) + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return true; + } + + /** + * {@inheritDoc} + */ + protected function doDelete($id) + { + return true; + } + + /** + * {@inheritDoc} + */ + protected function doFlush() + { + return true; + } + + /** + * {@inheritDoc} + */ + protected function doGetStats() + { + return; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php new file mode 100644 index 00000000000..8a250b29a87 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * WinCache cache provider. + * + * @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 wincache_ucache_set($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return wincache_ucache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return wincache_ucache_clear(); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return wincache_ucache_get($keys); + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + $result = wincache_ucache_set($keysAndValues, null, $lifetime); + + return empty($result); + } + + /** + * {@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_AVAILABLE => $meminfo['memory_free'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php new file mode 100644 index 00000000000..a2c4ca5662e --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Xcache cache driver. + * + * @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); + + return true; + } + + /** + * Checks that xcache.admin.enable_auth is Off. + * + * @return void + * + * @throws \BadMethodCallException When xcache.admin.enable_auth is On. + */ + 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_AVAILABLE => $info['avail'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php new file mode 100644 index 00000000000..6e35ac82369 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Zend Data Cache cache driver. + * + * @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/cache/phpunit.xml.dist b/vendor/doctrine/cache/phpunit.xml.dist new file mode 100644 index 00000000000..40cc24deea4 --- /dev/null +++ b/vendor/doctrine/cache/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php new file mode 100644 index 00000000000..29af44d257e --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php @@ -0,0 +1,21 @@ +markTestSkipped('The APC cache TTL is not working in a single process/request. See https://bugs.php.net/bug.php?id=58084'); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcuCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcuCacheTest.php new file mode 100644 index 00000000000..b0e91c3af5b --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcuCacheTest.php @@ -0,0 +1,21 @@ +markTestSkipped('The APC cache TTL is not working in a single process/request. See https://bugs.php.net/bug.php?id=58084'); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php new file mode 100644 index 00000000000..3be94c61755 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php @@ -0,0 +1,52 @@ +_getCacheDriver(); + $cache->fetch('test1'); + $cache->fetch('test2'); + $cache->fetch('test3'); + + $cache->save('test1', 123); + $cache->save('test2', 123); + + $cache->fetch('test1'); + $cache->fetch('test2'); + $cache->fetch('test3'); + + $stats = $cache->getStats(); + $this->assertEquals(2, $stats[Cache::STATS_HITS]); + $this->assertEquals(5, $stats[Cache::STATS_MISSES]); // +1 for internal call to DoctrineNamespaceCacheKey + $this->assertNotNull($stats[Cache::STATS_UPTIME]); + $this->assertNull($stats[Cache::STATS_MEMORY_USAGE]); + $this->assertNull($stats[Cache::STATS_MEMORY_AVAILABLE]); + + $cache->delete('test1'); + $cache->delete('test2'); + + $cache->fetch('test1'); + $cache->fetch('test2'); + $cache->fetch('test3'); + + $stats = $cache->getStats(); + $this->assertEquals(2, $stats[Cache::STATS_HITS]); + $this->assertEquals(8, $stats[Cache::STATS_MISSES]); // +1 for internal call to DoctrineNamespaceCacheKey + } + + protected function isSharedStorage() + { + return false; + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/BaseFileCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/BaseFileCacheTest.php new file mode 100644 index 00000000000..689452f24c8 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/BaseFileCacheTest.php @@ -0,0 +1,145 @@ +directory = sys_get_temp_dir() . '/doctrine_cache_'. uniqid(); + } while (file_exists($this->directory)); + } + + protected function tearDown() + { + if ( ! is_dir($this->directory)) { + return; + } + + $iterator = new RecursiveDirectoryIterator($this->directory); + + foreach (new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST) as $file) { + if ($file->isFile()) { + @unlink($file->getRealPath()); + } elseif ($file->isDir()) { + @rmdir($file->getRealPath()); + } + } + + @rmdir($this->directory); + } + + public function testFlushAllRemovesBalancingDirectories() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->flushAll()); + + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST); + + $this->assertCount(0, $iterator); + } + + protected function isSharedStorage() + { + return false; + } + + public function getPathLengthsToTest() + { + // Windows officially supports 260 bytes including null terminator + // 258 bytes available to use due to php bug #70943 + // Windows officially supports 260 bytes including null terminator + // 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943) + // 260 characters is too large - null terminator is included in allowable length + return array( + array(257, false), + array(258, false), + array(259, true), + array(260, true) + ); + } + + private static function getBasePathForWindowsPathLengthTests($pathLength) + { + return FileCacheTest::getBasePathForWindowsPathLengthTests($pathLength); + } + + private static function getKeyAndPathFittingLength($length) + { + $basePath = self::getBasePathForWindowsPathLengthTests($length); + + $baseDirLength = strlen($basePath); + $extensionLength = strlen('.doctrine.cache'); + $directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR); + $namespaceAndBracketLength = strlen(bin2hex("[][1]")); + $keyLength = $length + - ($baseDirLength + + $extensionLength + + $directoryLength + + $namespaceAndBracketLength); + + $key = str_repeat('a', floor($keyLength / 2)); + $namespacedKey = '[' . $key . '][1]'; + + $keyHash = hash('sha256', $namespacedKey); + + $keyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . bin2hex($namespacedKey) + . '.doctrine.cache'; + + $hashedKeyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . '_' . $keyHash + . '.doctrine.cache'; + + return array($key, $keyPath, $hashedKeyPath); + } + + /** + * @dataProvider getPathLengthsToTest + */ + public function testWindowsPathLengthLimitIsCorrectlyHandled($length, $pathShouldBeHashed) + { + $this->directory = self::getBasePathForWindowsPathLengthTests($length); + + list($key, $keyPath, $hashedKeyPath) = self::getKeyAndPathFittingLength($length); + + $this->assertEquals($length, strlen($keyPath), "Unhashed path should be of correct length."); + + $cacheClass = get_class($this->_getCacheDriver()); + $cache = new $cacheClass($this->directory, '.doctrine.cache'); + + // Trick it into thinking this is windows. + $reflClass = new \ReflectionClass('\Doctrine\Common\Cache\FileCache'); + $reflProp = $reflClass->getProperty('isRunningOnWindows'); + $reflProp->setAccessible(true); + $reflProp->setValue($cache, true); + $reflProp->setAccessible(false); + + $cache->save($key, $length); + $fetched = $cache->fetch($key); + $this->assertEquals($length, $fetched); + + if ($pathShouldBeHashed) { + $this->assertFileExists($hashedKeyPath, "Path generated for key should be hashed."); + unlink($hashedKeyPath); + } else { + $this->assertFileExists($keyPath, "Path generated for key should not be hashed."); + unlink($keyPath); + } + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheProviderTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheProviderTest.php new file mode 100644 index 00000000000..5e11652cc59 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheProviderTest.php @@ -0,0 +1,103 @@ +getMockForAbstractClass( + 'Doctrine\Common\Cache\CacheProvider', + array(), + '', + true, + true, + true, + array('doFetchMultiple') + ); + + $cache + ->expects($this->once()) + ->method('doFetchMultiple') + ->will($this->returnValue(array( + '[foo][1]' => 'bar', + '[bar][1]' => 'baz', + '[baz][1]' => 'tab', + ))); + + $this->assertEquals( + array('foo' => 'bar', 'bar' => 'baz'), + $cache->fetchMultiple(array('foo', 'bar')) + ); + } + + public function testFailedDeleteAllDoesNotChangeNamespaceVersion() + { + /* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */ + $cache = $this->getMockForAbstractClass( + 'Doctrine\Common\Cache\CacheProvider', + array(), + '', + true, + true, + true, + array('doFetch', 'doSave', 'doContains') + ); + + $cache + ->expects($this->once()) + ->method('doFetch') + ->with('DoctrineNamespaceCacheKey[]') + ->will($this->returnValue(false)); + + // doSave is only called once from deleteAll as we do not need to persist the default version in getNamespaceVersion() + $cache + ->expects($this->once()) + ->method('doSave') + ->with('DoctrineNamespaceCacheKey[]') + ->will($this->returnValue(false)); + + // After a failed deleteAll() the local namespace version is not increased (still 1). Otherwise all data written afterwards + // would be lost outside the current instance. + $cache + ->expects($this->once()) + ->method('doContains') + ->with('[key][1]') + ->will($this->returnValue(true)); + + $this->assertFalse($cache->deleteAll(), 'deleteAll() returns false when saving the namespace version fails'); + $cache->contains('key'); + } + + public function testSaveMultipleNoFail() + { + /* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */ + $cache = $this->getMockForAbstractClass( + 'Doctrine\Common\Cache\CacheProvider', + array(), + '', + true, + true, + true, + array('doSave') + ); + + $cache + ->expects($this->at(1)) + ->method('doSave') + ->with('[kerr][1]', 'verr', 0) + ->will($this->returnValue(false)); + + $cache + ->expects($this->at(2)) + ->method('doSave') + ->with('[kok][1]', 'vok', 0) + ->will($this->returnValue(true)); + + $cache->saveMultiple(array( + 'kerr' => 'verr', + 'kok' => 'vok', + )); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php new file mode 100644 index 00000000000..5dd64d7fd90 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php @@ -0,0 +1,463 @@ +_getCacheDriver(); + + // Test saving a value, checking if it exists, and fetching it back + $this->assertTrue($cache->save('key', $value)); + $this->assertTrue($cache->contains('key')); + if (is_object($value)) { + $this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference'); + } else { + $this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type'); + } + + // Test deleting a value + $this->assertTrue($cache->delete('key')); + $this->assertFalse($cache->contains('key')); + $this->assertFalse($cache->fetch('key')); + } + + /** + * @dataProvider provideDataToCache + */ + public function testUpdateExistingEntry($value) + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key', 'old-value')); + $this->assertTrue($cache->contains('key')); + + $this->assertTrue($cache->save('key', $value)); + $this->assertTrue($cache->contains('key')); + if (is_object($value)) { + $this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference'); + } else { + $this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type'); + } + } + + public function testCacheKeyIsCaseSensitive() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key', 'value')); + $this->assertTrue($cache->contains('key')); + $this->assertSame('value', $cache->fetch('key')); + + $this->assertFalse($cache->contains('KEY')); + $this->assertFalse($cache->fetch('KEY')); + + $cache->delete('KEY'); + $this->assertTrue($cache->contains('key', 'Deleting cache item with different case must not affect other cache item')); + } + + public function testFetchMultiple() + { + $cache = $this->_getCacheDriver(); + $values = $this->provideDataToCache(); + $saved = array(); + + foreach ($values as $key => $value) { + $cache->save($key, $value[0]); + + $saved[$key] = $value[0]; + } + + $keys = array_keys($saved); + + $this->assertEquals( + $saved, + $cache->fetchMultiple($keys), + 'Testing fetchMultiple with different data types' + ); + $this->assertEquals( + array_slice($saved, 0, 1), + $cache->fetchMultiple(array_slice($keys, 0, 1)), + 'Testing fetchMultiple with a single key' + ); + + $keysWithNonExisting = array(); + $keysWithNonExisting[] = 'non_existing1'; + $keysWithNonExisting[] = $keys[0]; + $keysWithNonExisting[] = 'non_existing2'; + $keysWithNonExisting[] = $keys[1]; + $keysWithNonExisting[] = 'non_existing3'; + + $this->assertEquals( + array_slice($saved, 0, 2), + $cache->fetchMultiple($keysWithNonExisting), + 'Testing fetchMultiple with a subset of keys and mixed with non-existing ones' + ); + } + + public function testFetchMultipleWithNoKeys() + { + $cache = $this->_getCacheDriver(); + + $this->assertSame(array(), $cache->fetchMultiple(array())); + } + + public function testSaveMultiple() + { + $cache = $this->_getCacheDriver(); + $cache->deleteAll(); + + $data = array_map(function ($value) { + return $value[0]; + }, $this->provideDataToCache()); + + $this->assertTrue($cache->saveMultiple($data)); + + $keys = array_keys($data); + + $this->assertEquals($data, $cache->fetchMultiple($keys)); + } + + public function provideDataToCache() + { + $obj = new \stdClass(); + $obj->foo = 'bar'; + $obj2 = new \stdClass(); + $obj2->bar = 'foo'; + $obj2->obj = $obj; + $obj->obj2 = $obj2; + + return array( + 'array' => array(array('one', 2, 3.01)), + 'string' => array('value'), + 'string_invalid_utf8' => array("\xc3\x28"), + 'string_null_byte' => array('with'."\0".'null char'), + 'integer' => array(1), + 'float' => array(1.5), + 'object' => array(new ArrayObject(array('one', 2, 3.01))), + 'object_recursive' => array($obj), + 'true' => array(true), + // the following are considered FALSE in boolean context, but caches should still recognize their existence + 'null' => array(null), + 'false' => array(false), + 'array_empty' => array(array()), + 'string_zero' => array('0'), + 'integer_zero' => array(0), + 'float_zero' => array(0.0), + 'string_empty' => array(''), + ); + } + + public function testDeleteIsSuccessfulWhenKeyDoesNotExist() + { + $cache = $this->_getCacheDriver(); + + $cache->delete('key'); + $this->assertFalse($cache->contains('key')); + $this->assertTrue($cache->delete('key')); + } + + public function testDeleteAll() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->deleteAll()); + $this->assertFalse($cache->contains('key1')); + $this->assertFalse($cache->contains('key2')); + } + + /** + * @dataProvider provideCacheIds + */ + public function testCanHandleSpecialCacheIds($id) + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save($id, 'value')); + $this->assertTrue($cache->contains($id)); + $this->assertEquals('value', $cache->fetch($id)); + + $this->assertTrue($cache->delete($id)); + $this->assertFalse($cache->contains($id)); + $this->assertFalse($cache->fetch($id)); + } + + public function testNoCacheIdCollisions() + { + $cache = $this->_getCacheDriver(); + + $ids = $this->provideCacheIds(); + + // fill cache with each id having a different value + foreach ($ids as $index => $id) { + $cache->save($id[0], $index); + } + + // then check value of each cache id + foreach ($ids as $index => $id) { + $value = $cache->fetch($id[0]); + $this->assertNotFalse($value, sprintf('Failed to retrieve data for cache id "%s".', $id[0])); + if ($index !== $value) { + $this->fail(sprintf('Cache id "%s" collides with id "%s".', $id[0], $ids[$value][0])); + } + } + } + + /** + * Returns cache ids with special characters that should still work. + * + * For example, the characters :\/<>"*?| are not valid in Windows filenames. So they must be encoded properly. + * Each cache id should be considered different from the others. + * + * @return array + */ + public function provideCacheIds() + { + return array( + array(':'), + array('\\'), + array('/'), + array('<'), + array('>'), + array('"'), + array('*'), + array('?'), + array('|'), + array('['), + array(']'), + array('ä'), + array('a'), + array('é'), + array('e'), + array('.'), // directory traversal + array('..'), // directory traversal + array('-'), + array('_'), + array('$'), + array('%'), + array(' '), + array("\0"), + array(''), + array(str_repeat('a', 300)), // long key + array(str_repeat('a', 113)), + ); + } + + public function testLifetime() + { + $cache = $this->_getCacheDriver(); + $cache->save('expire', 'value', 1); + $this->assertTrue($cache->contains('expire'), 'Data should not be expired yet'); + // @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead + sleep(2); + $this->assertFalse($cache->contains('expire'), 'Data should be expired'); + } + + public function testNoExpire() + { + $cache = $this->_getCacheDriver(); + $cache->save('noexpire', 'value', 0); + // @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead + sleep(1); + $this->assertTrue($cache->contains('noexpire'), 'Data with lifetime of zero should not expire'); + } + + public function testLongLifetime() + { + $cache = $this->_getCacheDriver(); + $cache->save('longlifetime', 'value', 30 * 24 * 3600 + 1); + $this->assertTrue($cache->contains('longlifetime'), 'Data with lifetime > 30 days should be accepted'); + } + + public function testDeleteAllAndNamespaceVersioningBetweenCaches() + { + if ( ! $this->isSharedStorage()) { + $this->markTestSkipped('The cache storage needs to be shared.'); + } + + $cache1 = $this->_getCacheDriver(); + $cache2 = $this->_getCacheDriver(); + + $this->assertTrue($cache1->save('key1', 1)); + $this->assertTrue($cache2->save('key2', 2)); + + /* Both providers are initialized with the same namespace version, so + * they can see entries set by each other. + */ + $this->assertTrue($cache1->contains('key1')); + $this->assertTrue($cache1->contains('key2')); + $this->assertTrue($cache2->contains('key1')); + $this->assertTrue($cache2->contains('key2')); + + /* Deleting all entries through one provider will only increment the + * namespace version on that object (and in the cache itself, which new + * instances will use to initialize). The second provider will retain + * its original version and still see stale data. + */ + $this->assertTrue($cache1->deleteAll()); + $this->assertFalse($cache1->contains('key1')); + $this->assertFalse($cache1->contains('key2')); + $this->assertTrue($cache2->contains('key1')); + $this->assertTrue($cache2->contains('key2')); + + /* A new cache provider should not see the deleted entries, since its + * namespace version will be initialized. + */ + $cache3 = $this->_getCacheDriver(); + $this->assertFalse($cache3->contains('key1')); + $this->assertFalse($cache3->contains('key2')); + } + + public function testFlushAll() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->flushAll()); + $this->assertFalse($cache->contains('key1')); + $this->assertFalse($cache->contains('key2')); + } + + public function testFlushAllAndNamespaceVersioningBetweenCaches() + { + if ( ! $this->isSharedStorage()) { + $this->markTestSkipped('The cache storage needs to be shared.'); + } + + $cache1 = $this->_getCacheDriver(); + $cache2 = $this->_getCacheDriver(); + + /* Deleting all elements from the first provider should increment its + * namespace version before saving the first entry. + */ + $cache1->deleteAll(); + $this->assertTrue($cache1->save('key1', 1)); + + /* The second provider will be initialized with the same namespace + * version upon its first save operation. + */ + $this->assertTrue($cache2->save('key2', 2)); + + /* Both providers have the same namespace version and can see entries + * set by each other. + */ + $this->assertTrue($cache1->contains('key1')); + $this->assertTrue($cache1->contains('key2')); + $this->assertTrue($cache2->contains('key1')); + $this->assertTrue($cache2->contains('key2')); + + /* Flushing all entries through one cache will remove all entries from + * the cache but leave their namespace version as-is. + */ + $this->assertTrue($cache1->flushAll()); + $this->assertFalse($cache1->contains('key1')); + $this->assertFalse($cache1->contains('key2')); + $this->assertFalse($cache2->contains('key1')); + $this->assertFalse($cache2->contains('key2')); + + /* Inserting a new entry will use the same, incremented namespace + * version, and it will be visible to both providers. + */ + $this->assertTrue($cache1->save('key1', 1)); + $this->assertTrue($cache1->contains('key1')); + $this->assertTrue($cache2->contains('key1')); + + /* A new cache provider will be initialized with the original namespace + * version and not share any visibility with the first two providers. + */ + $cache3 = $this->_getCacheDriver(); + $this->assertFalse($cache3->contains('key1')); + $this->assertFalse($cache3->contains('key2')); + $this->assertTrue($cache3->save('key3', 3)); + $this->assertTrue($cache3->contains('key3')); + } + + public function testNamespace() + { + $cache = $this->_getCacheDriver(); + + $cache->setNamespace('ns1_'); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('ns2_'); + + $this->assertFalse($cache->contains('key1')); + } + + public function testDeleteAllNamespace() + { + $cache = $this->_getCacheDriver(); + + $cache->setNamespace('ns1'); + $this->assertFalse($cache->contains('key1')); + $cache->save('key1', 'test'); + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('ns2'); + $this->assertFalse($cache->contains('key1')); + $cache->save('key1', 'test'); + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('ns1'); + $this->assertTrue($cache->contains('key1')); + $cache->deleteAll(); + $this->assertFalse($cache->contains('key1')); + + $cache->setNamespace('ns2'); + $this->assertTrue($cache->contains('key1')); + $cache->deleteAll(); + $this->assertFalse($cache->contains('key1')); + } + + /** + * @group DCOM-43 + */ + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertArrayHasKey(Cache::STATS_HITS, $stats); + $this->assertArrayHasKey(Cache::STATS_MISSES, $stats); + $this->assertArrayHasKey(Cache::STATS_UPTIME, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_USAGE, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_AVAILABLE, $stats); + } + + public function testSaveReturnsTrueWithAndWithoutTTlSet() + { + $cache = $this->_getCacheDriver(); + $cache->deleteAll(); + $this->assertTrue($cache->save('without_ttl', 'without_ttl')); + $this->assertTrue($cache->save('with_ttl', 'with_ttl', 3600)); + } + + /** + * Return whether multiple cache providers share the same storage. + * + * This is used for skipping certain tests for shared storage behavior. + * + * @return bool + */ + protected function isSharedStorage() + { + return true; + } + + /** + * @return \Doctrine\Common\Cache\CacheProvider + */ + abstract protected function _getCacheDriver(); +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ChainCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ChainCacheTest.php new file mode 100644 index 00000000000..a3c013b8dd0 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ChainCacheTest.php @@ -0,0 +1,99 @@ +markTestSkipped('The ChainCache test uses ArrayCache which does not implement TTL currently.'); + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertInternalType('array', $stats); + } + + public function testOnlyFetchFirstOne() + { + $cache1 = new ArrayCache(); + $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + + $cache2->expects($this->never())->method('doFetch'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->save('id', 'bar'); + + $this->assertEquals('bar', $chainCache->fetch('id')); + } + + public function testFetchPropagateToFastestCache() + { + $cache1 = new ArrayCache(); + $cache2 = new ArrayCache(); + + $cache2->save('bar', 'value'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + + $this->assertFalse($cache1->contains('bar')); + + $result = $chainCache->fetch('bar'); + + $this->assertEquals('value', $result); + $this->assertTrue($cache2->contains('bar')); + } + + public function testNamespaceIsPropagatedToAllProviders() + { + $cache1 = new ArrayCache(); + $cache2 = new ArrayCache(); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->setNamespace('bar'); + + $this->assertEquals('bar', $cache1->getNamespace()); + $this->assertEquals('bar', $cache2->getNamespace()); + } + + public function testDeleteToAllProviders() + { + $cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + + $cache1->expects($this->once())->method('doDelete'); + $cache2->expects($this->once())->method('doDelete'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->delete('bar'); + } + + public function testFlushToAllProviders() + { + $cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + + $cache1->expects($this->once())->method('doFlush'); + $cache2->expects($this->once())->method('doFlush'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->flushAll(); + } + + protected function isSharedStorage() + { + return false; + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php new file mode 100644 index 00000000000..f42b3a7f7f5 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php @@ -0,0 +1,30 @@ +couchbase = new Couchbase('127.0.0.1', 'Administrator', 'password', 'default'); + } catch(Exception $ex) { + $this->markTestSkipped('Could not instantiate the Couchbase cache because of: ' . $ex); + } + } + + protected function _getCacheDriver() + { + $driver = new CouchbaseCache(); + $driver->setCouchbase($this->couchbase); + return $driver; + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FileCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FileCacheTest.php new file mode 100644 index 00000000000..c93b50925ad --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FileCacheTest.php @@ -0,0 +1,256 @@ +driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(), '', false + ); + } + + public function testFilenameShouldCreateThePathWithOneSubDirectory() + { + $cache = $this->driver; + $method = new \ReflectionMethod($cache, 'getFilename'); + $key = 'item-key'; + $expectedDir = array( + '84', + ); + $expectedDir = implode(DIRECTORY_SEPARATOR, $expectedDir); + + $method->setAccessible(true); + + $path = $method->invoke($cache, $key); + $dirname = pathinfo($path, PATHINFO_DIRNAME); + + $this->assertEquals(DIRECTORY_SEPARATOR . $expectedDir, $dirname); + } + + public function testFileExtensionCorrectlyEscaped() + { + $driver1 = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(__DIR__, '.*') + ); + $driver2 = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(__DIR__, '.php') + ); + + $doGetStats = new \ReflectionMethod($driver1, 'doGetStats'); + + $doGetStats->setAccessible(true); + + $stats1 = $doGetStats->invoke($driver1); + $stats2 = $doGetStats->invoke($driver2); + + $this->assertSame(0, $stats1[Cache::STATS_MEMORY_USAGE]); + $this->assertGreaterThan(0, $stats2[Cache::STATS_MEMORY_USAGE]); + } + + /** + * @group DCOM-266 + */ + public function testFileExtensionSlashCorrectlyEscaped() + { + $driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(__DIR__ . '/../', DIRECTORY_SEPARATOR . basename(__FILE__)) + ); + + $doGetStats = new \ReflectionMethod($driver, 'doGetStats'); + + $doGetStats->setAccessible(true); + + $stats = $doGetStats->invoke($driver); + + $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_USAGE]); + } + + public function testNonIntUmaskThrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + + $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array('', '', 'invalid') + ); + } + + public function testGetDirectoryReturnsRealpathDirectoryString() + { + $directory = __DIR__ . '/../'; + $driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array($directory) + ); + + $doGetDirectory = new \ReflectionMethod($driver, 'getDirectory'); + + $actualDirectory = $doGetDirectory->invoke($driver); + $expectedDirectory = realpath($directory); + + $this->assertEquals($expectedDirectory, $actualDirectory); + } + + public function testGetExtensionReturnsExtensionString() + { + $directory = __DIR__ . '/../'; + $extension = DIRECTORY_SEPARATOR . basename(__FILE__); + $driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array($directory, $extension) + ); + + $doGetExtension = new \ReflectionMethod($driver, 'getExtension'); + + $actualExtension = $doGetExtension->invoke($driver); + + $this->assertEquals($extension, $actualExtension); + } + + const WIN_MAX_PATH_LEN = 258; + + public static function getBasePathForWindowsPathLengthTests($pathLength) + { + // Not using __DIR__ because it can get screwed up when xdebug debugger is attached. + $basePath = realpath(sys_get_temp_dir()); + + // Test whether the desired path length is odd or even. + $desiredPathLengthIsOdd = ($pathLength % 2) == 1; + + // If the cache key is not too long, the filecache codepath will add + // a slash and bin2hex($key). The length of the added portion will be an odd number. + // len(desired) = len(base path) + len(slash . bin2hex($key)) + // odd = even + odd + // even = odd + odd + $basePathLengthShouldBeOdd = !$desiredPathLengthIsOdd; + + $basePathLengthIsOdd = (strlen($basePath) % 2) == 1; + + // If the base path needs to be odd or even where it is not, we add an odd number of + // characters as a pad. In this case, we're adding '\aa' (or '/aa' depending on platform) + // This is all to make it so that the key we're testing would result in + // a path that is exactly the length we want to test IF the path length limit + // were not in place in FileCache. + if ($basePathLengthIsOdd != $basePathLengthShouldBeOdd) { + $basePath .= DIRECTORY_SEPARATOR . "aa"; + } + + return $basePath; + } + + public static function getKeyAndPathFittingLength($length) + { + $basePath = self::getBasePathForWindowsPathLengthTests($length); + + $baseDirLength = strlen($basePath); + $extensionLength = strlen('.doctrine.cache'); + $directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR); + $keyLength = $length - ($baseDirLength + $extensionLength + $directoryLength); // - 1 because of slash + + $key = str_repeat('a', floor($keyLength / 2)); + + $keyHash = hash('sha256', $key); + + $keyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . bin2hex($key) + . '.doctrine.cache'; + + $hashedKeyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . '_' . $keyHash + . '.doctrine.cache'; + + return array($key, $keyPath, $hashedKeyPath); + } + + public function getPathLengthsToTest() + { + // Windows officially supports 260 bytes including null terminator + // 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943) + // 260 characters is too large - null terminator is included in allowable length + return array( + array(257, false), + array(258, false), + array(259, true), + array(260, true) + ); + } + + /** + * @runInSeparateProcess + * @dataProvider getPathLengthsToTest + * + * @covers \Doctrine\Common\Cache\FileCache::getFilename + */ + public function testWindowsPathLengthLimitationsAreCorrectlyRespected($length, $pathShouldBeHashed) + { + if (! defined('PHP_WINDOWS_VERSION_BUILD')) { + define('PHP_WINDOWS_VERSION_BUILD', 'Yes, this is the "usual suspect", with the usual limitations'); + } + + $basePath = $this->getBasePathForWindowsPathLengthTests($length); + + $fileCache = $this->getMockForAbstractClass( + 'Doctrine\Common\Cache\FileCache', + array($basePath, '.doctrine.cache') + ); + + list($key, $keyPath, $hashedKeyPath) = $this->getKeyAndPathFittingLength($length); + + $getFileName = new \ReflectionMethod($fileCache, 'getFilename'); + + $getFileName->setAccessible(true); + + $this->assertEquals( + $length, + strlen($keyPath), + sprintf('Path expected to be %d characters long is %d characters long', $length, strlen($keyPath)) + ); + + if ($pathShouldBeHashed) { + $keyPath = $hashedKeyPath; + } + + if ($pathShouldBeHashed) { + $this->assertSame( + $hashedKeyPath, + $getFileName->invoke($fileCache, $key), + 'Keys should be hashed correctly if they are over the limit.' + ); + } else { + $this->assertSame( + $keyPath, + $getFileName->invoke($fileCache, $key), + 'Keys below limit of the allowed length are used directly, unhashed' + ); + } + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php new file mode 100644 index 00000000000..9e7c9e81851 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php @@ -0,0 +1,61 @@ +_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats[Cache::STATS_HITS]); + $this->assertNull($stats[Cache::STATS_MISSES]); + $this->assertNull($stats[Cache::STATS_UPTIME]); + $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); + $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]); + } + + public function testCacheInSharedDirectoryIsPerExtension() + { + $cache1 = new FilesystemCache($this->directory, '.foo'); + $cache2 = new FilesystemCache($this->directory, '.bar'); + + $this->assertTrue($cache1->save('key1', 11)); + $this->assertTrue($cache1->save('key2', 12)); + + $this->assertTrue($cache2->save('key1', 21)); + $this->assertTrue($cache2->save('key2', 22)); + + $this->assertSame(11, $cache1->fetch('key1'), 'Cache value must not be influenced by a different cache in the same directory but different extension'); + $this->assertSame(12, $cache1->fetch('key2')); + $this->assertTrue($cache1->flushAll()); + $this->assertFalse($cache1->fetch('key1'), 'flushAll() must delete all items with the current extension'); + $this->assertFalse($cache1->fetch('key2')); + + $this->assertSame(21, $cache2->fetch('key1'), 'flushAll() must not remove items with a different extension in a shared directory'); + $this->assertSame(22, $cache2->fetch('key2')); + } + + public function testFlushAllWithNoExtension() + { + $cache = new FilesystemCache($this->directory, ''); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->flushAll()); + $this->assertFalse($cache->contains('key1')); + $this->assertFalse($cache->contains('key2')); + } + + protected function _getCacheDriver() + { + return new FilesystemCache($this->directory); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php new file mode 100644 index 00000000000..74853b8491f --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php @@ -0,0 +1,59 @@ +memcache = new Memcache(); + + if (@$this->memcache->connect('localhost', 11211) === false) { + unset($this->memcache); + $this->markTestSkipped('Cannot connect to Memcache.'); + } + } + + protected function tearDown() + { + if ($this->memcache instanceof Memcache) { + $this->memcache->flush(); + } + } + + /** + * {@inheritdoc} + * + * Memcache does not support " " and null byte as key so we remove them from the tests. + */ + public function provideCacheIds() + { + $ids = parent::provideCacheIds(); + unset($ids[21], $ids[22]); + + return $ids; + } + + public function testGetMemcacheReturnsInstanceOfMemcache() + { + $this->assertInstanceOf('Memcache', $this->_getCacheDriver()->getMemcache()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + $driver = new MemcacheCache(); + $driver->setMemcache($this->memcache); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php new file mode 100644 index 00000000000..fbcf5c38edd --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php @@ -0,0 +1,61 @@ +memcached = new Memcached(); + $this->memcached->setOption(Memcached::OPT_COMPRESSION, false); + $this->memcached->addServer('127.0.0.1', 11211); + + if (@fsockopen('127.0.0.1', 11211) === false) { + unset($this->memcached); + $this->markTestSkipped('Cannot connect to Memcached.'); + } + } + + protected function tearDown() + { + if ($this->memcached instanceof Memcached) { + $this->memcached->flush(); + } + } + + /** + * {@inheritdoc} + * + * Memcached does not support " ", null byte and very long keys so we remove them from the tests. + */ + public function provideCacheIds() + { + $ids = parent::provideCacheIds(); + unset($ids[21], $ids[22], $ids[24]); + + return $ids; + } + + public function testGetMemcachedReturnsInstanceOfMemcached() + { + $this->assertInstanceOf('Memcached', $this->_getCacheDriver()->getMemcached()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + $driver = new MemcachedCache(); + $driver->setMemcached($this->memcached); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MongoDBCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MongoDBCacheTest.php new file mode 100644 index 00000000000..fa7cfacd1d3 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MongoDBCacheTest.php @@ -0,0 +1,68 @@ +=')) { + $this->markTestSkipped('Mongo >= 1.3.0 is required.'); + } + + $mongo = new MongoClient(); + $this->collection = $mongo->selectCollection('doctrine_common_cache', 'test'); + } + + protected function tearDown() + { + if ($this->collection instanceof MongoCollection) { + $this->collection->drop(); + } + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats[Cache::STATS_HITS]); + $this->assertNull($stats[Cache::STATS_MISSES]); + $this->assertGreaterThan(0, $stats[Cache::STATS_UPTIME]); + $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); + $this->assertNull($stats[Cache::STATS_MEMORY_AVAILABLE]); + } + + /** + * @group 108 + */ + public function testMongoCursorExceptionsDoNotBubbleUp() + { + /* @var $collection \MongoCollection|\PHPUnit_Framework_MockObject_MockObject */ + $collection = $this->getMock('MongoCollection', array(), array(), '', false); + + $collection->expects(self::once())->method('update')->willThrowException(new \MongoCursorException()); + + $cache = new MongoDBCache($collection); + + self::assertFalse($cache->save('foo', 'bar')); + } + + protected function _getCacheDriver() + { + return new MongoDBCache($this->collection); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php new file mode 100644 index 00000000000..32eec6e28fe --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php @@ -0,0 +1,95 @@ +_getCacheDriver(); + + // Test save + $cache->save('test_set_state', new SetStateClass(array(1,2,3))); + + //Test __set_state call + $this->assertCount(0, SetStateClass::$values); + + // Test fetch + $value = $cache->fetch('test_set_state'); + $this->assertInstanceOf('Doctrine\Tests\Common\Cache\SetStateClass', $value); + $this->assertEquals(array(1,2,3), $value->getValue()); + + //Test __set_state call + $this->assertCount(1, SetStateClass::$values); + + // Test contains + $this->assertTrue($cache->contains('test_set_state')); + } + + public function testNotImplementsSetState() + { + $cache = $this->_getCacheDriver(); + + $this->setExpectedException('InvalidArgumentException'); + $cache->save('test_not_set_state', new NotSetStateClass(array(1,2,3))); + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats[Cache::STATS_HITS]); + $this->assertNull($stats[Cache::STATS_MISSES]); + $this->assertNull($stats[Cache::STATS_UPTIME]); + $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); + $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]); + } + + protected function _getCacheDriver() + { + return new PhpFileCache($this->directory); + } +} + +class NotSetStateClass +{ + private $value; + + public function __construct($value) + { + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } +} + +class SetStateClass extends NotSetStateClass +{ + public static $values = array(); + + public static function __set_state($data) + { + self::$values = $data; + return new self($data['value']); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PredisCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PredisCacheTest.php new file mode 100644 index 00000000000..e20839dc097 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PredisCacheTest.php @@ -0,0 +1,87 @@ +markTestSkipped('Predis\Client is missing. Make sure to "composer install" to have all dev dependencies.'); + } + + $this->client = new Client(); + + try { + $this->client->connect(); + } catch (ConnectionException $e) { + $this->markTestSkipped('Cannot connect to Redis because of: ' . $e); + } + } + + public function testHitMissesStatsAreProvided() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNotNull($stats[Cache::STATS_HITS]); + $this->assertNotNull($stats[Cache::STATS_MISSES]); + } + + /** + * @return PredisCache + */ + protected function _getCacheDriver() + { + return new PredisCache($this->client); + } + + /** + * {@inheritDoc} + * + * @dataProvider provideDataToCache + */ + public function testSetContainsFetchDelete($value) + { + if (array() === $value) { + $this->markTestIncomplete( + 'Predis currently doesn\'t support saving empty array values. ' + . 'See https://github.com/nrk/predis/issues/241' + ); + } + + parent::testSetContainsFetchDelete($value); + } + + /** + * {@inheritDoc} + * + * @dataProvider provideDataToCache + */ + public function testUpdateExistingEntry($value) + { + if (array() === $value) { + $this->markTestIncomplete( + 'Predis currently doesn\'t support saving empty array values. ' + . 'See https://github.com/nrk/predis/issues/241' + ); + } + + parent::testUpdateExistingEntry($value); + } + + public function testAllowsGenericPredisClient() + { + /* @var $predisClient \Predis\ClientInterface */ + $predisClient = $this->getMock('Predis\\ClientInterface'); + + $this->assertInstanceOf('Doctrine\\Common\\Cache\\PredisCache', new PredisCache($predisClient)); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php new file mode 100644 index 00000000000..caaaa062909 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php @@ -0,0 +1,47 @@ +_redis = new \Redis(); + $ok = @$this->_redis->connect('127.0.0.1'); + if (!$ok) { + $this->markTestSkipped('Cannot connect to Redis.'); + } + } + + public function testHitMissesStatsAreProvided() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNotNull($stats[Cache::STATS_HITS]); + $this->assertNotNull($stats[Cache::STATS_MISSES]); + } + + public function testGetRedisReturnsInstanceOfRedis() + { + $this->assertInstanceOf('Redis', $this->_getCacheDriver()->getRedis()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + $driver = new RedisCache(); + $driver->setRedis($this->_redis); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RiakCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RiakCacheTest.php new file mode 100644 index 00000000000..e7d5f3dd33d --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RiakCacheTest.php @@ -0,0 +1,58 @@ +connection = new Connection('127.0.0.1', 8087); + $this->bucket = new Bucket($this->connection, 'test'); + } catch (Exception\RiakException $e) { + $this->markTestSkipped('Cannot connect to Riak.'); + } + } + + /** + * {@inheritdoc} + */ + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + /** + * Retrieve RiakCache instance. + * + * @return \Doctrine\Common\Cache\RiakCache + */ + protected function _getCacheDriver() + { + return new RiakCache($this->bucket); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/SQLite3CacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/SQLite3CacheTest.php new file mode 100644 index 00000000000..f0c5b0b592a --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/SQLite3CacheTest.php @@ -0,0 +1,42 @@ +file = tempnam(null, 'doctrine-cache-test-'); + unlink($this->file); + $this->sqlite = new SQLite3($this->file); + } + + protected function tearDown() + { + $this->sqlite = null; // DB must be closed before + unlink($this->file); + } + + public function testGetStats() + { + $this->assertNull($this->_getCacheDriver()->getStats()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + return new SQLite3Cache($this->sqlite, 'test_table'); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/VoidCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/VoidCacheTest.php new file mode 100644 index 00000000000..8ab7e5b4721 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/VoidCacheTest.php @@ -0,0 +1,58 @@ +assertFalse($cache->contains('foo')); + $this->assertFalse($cache->contains('bar')); + } + + public function testShouldAlwaysReturnFalseOnFetch() + { + $cache = new VoidCache(); + + $this->assertFalse($cache->fetch('foo')); + $this->assertFalse($cache->fetch('bar')); + } + + public function testShouldAlwaysReturnTrueOnSaveButNotStoreAnything() + { + $cache = new VoidCache(); + + $this->assertTrue($cache->save('foo', 'fooVal')); + + $this->assertFalse($cache->contains('foo')); + $this->assertFalse($cache->fetch('foo')); + } + + public function testShouldAlwaysReturnTrueOnDelete() + { + $cache = new VoidCache(); + + $this->assertTrue($cache->delete('foo')); + } + + public function testShouldAlwaysReturnNullOnGetStatus() + { + $cache = new VoidCache(); + + $this->assertNull($cache->getStats()); + } + + public function testShouldAlwaysReturnTrueOnFlush() + { + $cache = new VoidCache(); + + $this->assertTrue($cache->flushAll()); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php new file mode 100644 index 00000000000..1dded424789 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php @@ -0,0 +1,16 @@ +markTestSkipped('Zend Data Cache only works in apache2handler SAPI.'); + } + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + protected function _getCacheDriver() + { + return new ZendDataCache(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php b/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php new file mode 100644 index 00000000000..e8323d29409 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php @@ -0,0 +1,10 @@ + + + + + + + + + + + + + + ../Doctrine/ + + + + + + ../../lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/collections/.gitignore b/vendor/doctrine/collections/.gitignore new file mode 100644 index 00000000000..48b8bf9072d --- /dev/null +++ b/vendor/doctrine/collections/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/vendor/doctrine/collections/.travis.yml b/vendor/doctrine/collections/.travis.yml new file mode 100644 index 00000000000..60f479277d0 --- /dev/null +++ b/vendor/doctrine/collections/.travis.yml @@ -0,0 +1,21 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + - hhvm-nightly + +matrix: + fast_finish: true + allow_failures: + - php: 7.0 + +before_script: + - composer --prefer-source install + +script: + - phpunit diff --git a/vendor/doctrine/collections/LICENSE b/vendor/doctrine/collections/LICENSE new file mode 100644 index 00000000000..5e781fce4bb --- /dev/null +++ b/vendor/doctrine/collections/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 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/collections/README.md b/vendor/doctrine/collections/README.md new file mode 100644 index 00000000000..161cab63c73 --- /dev/null +++ b/vendor/doctrine/collections/README.md @@ -0,0 +1,25 @@ +# Doctrine Collections + +[![Build Status](https://travis-ci.org/doctrine/collections.svg?branch=master)](https://travis-ci.org/doctrine/collections) + +Collections Abstraction library + +## Changelog + +### v1.3.0 + +* [Explicit casting of first and max results in criteria API](https://github.com/doctrine/collections/pull/26) +* [Keep keys when using `ArrayCollection#matching()` with sorting](https://github.com/doctrine/collections/pull/49) +* [Made `AbstractLazyCollection#$initialized` protected for extensibility](https://github.com/doctrine/collections/pull/52) + +### v1.2.0 + +* Add a new ``AbstractLazyCollection`` + +### v1.1.0 + +* Deprecated ``Comparison::IS``, because it's only there for SQL semantics. + These are fixed in the ORM instead. +* Add ``Comparison::CONTAINS`` to perform partial string matches: + + $criteria->andWhere($criteria->expr()->contains('property', 'Foo')); diff --git a/vendor/doctrine/collections/composer.json b/vendor/doctrine/collections/composer.json new file mode 100644 index 00000000000..155cac48f97 --- /dev/null +++ b/vendor/doctrine/collections/composer.json @@ -0,0 +1,29 @@ +{ + "name": "doctrine/collections", + "type": "library", + "description": "Collections Abstraction library", + "keywords": ["collections", "array", "iterator"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "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" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php new file mode 100644 index 00000000000..b907f8b02a0 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php @@ -0,0 +1,343 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure; + +/** + * Lazy collection that is backed by a concrete collection + * + * @author Michaël Gallego + * @since 1.2 + */ +abstract class AbstractLazyCollection implements Collection +{ + /** + * The backed collection to use + * + * @var Collection + */ + protected $collection; + + /** + * @var boolean + */ + protected $initialized = false; + + /** + * {@inheritDoc} + */ + public function count() + { + $this->initialize(); + return $this->collection->count(); + } + + /** + * {@inheritDoc} + */ + public function add($element) + { + $this->initialize(); + return $this->collection->add($element); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->initialize(); + $this->collection->clear(); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + $this->initialize(); + return $this->collection->contains($element); + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + $this->initialize(); + return $this->collection->isEmpty(); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + $this->initialize(); + return $this->collection->remove($key); + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $this->initialize(); + return $this->collection->removeElement($element); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + $this->initialize(); + return $this->collection->containsKey($key); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + $this->initialize(); + return $this->collection->get($key); + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + $this->initialize(); + return $this->collection->getKeys(); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + $this->initialize(); + return $this->collection->getValues(); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->initialize(); + $this->collection->set($key, $value); + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + $this->initialize(); + return $this->collection->toArray(); + } + + /** + * {@inheritDoc} + */ + public function first() + { + $this->initialize(); + return $this->collection->first(); + } + + /** + * {@inheritDoc} + */ + public function last() + { + $this->initialize(); + return $this->collection->last(); + } + + /** + * {@inheritDoc} + */ + public function key() + { + $this->initialize(); + return $this->collection->key(); + } + + /** + * {@inheritDoc} + */ + public function current() + { + $this->initialize(); + return $this->collection->current(); + } + + /** + * {@inheritDoc} + */ + public function next() + { + $this->initialize(); + return $this->collection->next(); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + return $this->collection->exists($p); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + return $this->collection->filter($p); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + return $this->collection->forAll($p); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + $this->initialize(); + return $this->collection->map($func); + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + return $this->collection->partition($p); + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + $this->initialize(); + return $this->collection->indexOf($element); + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + $this->initialize(); + return $this->collection->slice($offset, $length); + } + + /** + * {@inheritDoc} + */ + public function getIterator() + { + $this->initialize(); + return $this->collection->getIterator(); + } + + /** + * {@inheritDoc} + */ + public function offsetExists($offset) + { + $this->initialize(); + return $this->collection->offsetExists($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetGet($offset) + { + $this->initialize(); + return $this->collection->offsetGet($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + $this->initialize(); + $this->collection->offsetSet($offset, $value); + } + + /** + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + $this->initialize(); + $this->collection->offsetUnset($offset); + } + + /** + * Is the lazy collection already initialized? + * + * @return bool + */ + public function isInitialized() + { + return $this->initialized; + } + + /** + * Initialize the collection + * + * @return void + */ + protected function initialize() + { + if ( ! $this->initialized) { + $this->doInitialize(); + $this->initialized = true; + } + } + + /** + * Do the initialization logic + * + * @return void + */ + abstract protected function doInitialize(); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php new file mode 100644 index 00000000000..bce575120ea --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -0,0 +1,387 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use ArrayIterator; +use Closure; +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; + +/** + * 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, Selectable +{ + /** + * 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; + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + return $this->elements; + } + + /** + * {@inheritDoc} + */ + public function first() + { + return reset($this->elements); + } + + /** + * {@inheritDoc} + */ + public function last() + { + return end($this->elements); + } + + /** + * {@inheritDoc} + */ + public function key() + { + return key($this->elements); + } + + /** + * {@inheritDoc} + */ + public function next() + { + return next($this->elements); + } + + /** + * {@inheritDoc} + */ + public function current() + { + return current($this->elements); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + if ( ! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) { + return null; + } + + $removed = $this->elements[$key]; + unset($this->elements[$key]); + + return $removed; + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $key = array_search($element, $this->elements, true); + + if ($key === false) { + return false; + } + + unset($this->elements[$key]); + + return true; + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + + $this->set($offset, $value); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + return isset($this->elements[$key]) || array_key_exists($key, $this->elements); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + return in_array($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + return array_search($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + return isset($this->elements[$key]) ? $this->elements[$key] : null; + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + return array_keys($this->elements); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + return array_values($this->elements); + } + + /** + * {@inheritDoc} + */ + public function count() + { + return count($this->elements); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->elements[$key] = $value; + } + + /** + * {@inheritDoc} + */ + public function add($value) + { + $this->elements[] = $value; + + return true; + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + return empty($this->elements); + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritDoc} + */ + public function getIterator() + { + return new ArrayIterator($this->elements); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + return new static(array_map($func, $this->elements)); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + return new static(array_filter($this->elements, $p)); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ( ! $p($key, $element)) { + return false; + } + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $matches = $noMatches = array(); + + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + $matches[$key] = $element; + } else { + $noMatches[$key] = $element; + } + } + + return array(new static($matches), new static($noMatches)); + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->elements = array(); + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + return array_slice($this->elements, $offset, $length, true); + } + + /** + * {@inheritDoc} + */ + public function matching(Criteria $criteria) + { + $expr = $criteria->getWhereExpression(); + $filtered = $this->elements; + + if ($expr) { + $visitor = new ClosureExpressionVisitor(); + $filter = $visitor->dispatch($expr); + $filtered = array_filter($filtered, $filter); + } + + if ($orderings = $criteria->getOrderings()) { + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1); + } + + uasort($filtered, $next); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset || $length) { + $filtered = array_slice($filtered, (int)$offset, $length); + } + + return new static($filtered); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php new file mode 100644 index 00000000000..8792f7a5b66 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php @@ -0,0 +1,263 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use ArrayAccess; +use Closure; +use Countable; +use IteratorAggregate; + +/** + * 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 preferable. + * 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. + */ + public function add($element); + + /** + * Clears the collection, removing all elements. + * + * @return void + */ + public 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. + */ + public function contains($element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + public 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. + */ + public 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. + */ + public 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. + */ + public 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 + */ + public 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. + */ + public 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. + */ + public 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. + * + * @return void + */ + public function set($key, $value); + + /** + * Gets a native PHP array representation of the collection. + * + * @return array + */ + public function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and returns this element. + * + * @return mixed + */ + public function first(); + + /** + * Sets the internal iterator to the last element in the collection and returns this element. + * + * @return mixed + */ + public function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + * @return int|string + */ + public function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + * @return mixed + */ + public function current(); + + /** + * Moves the internal iterator position to the next element and returns this element. + * + * @return mixed + */ + public 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. + */ + public 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. + */ + public function filter(Closure $p); + + /** + * Tests whether the given predicate p holds for all elements of this collection. + * + * @param Closure $p The predicate. + * + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public 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 + */ + public 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. + */ + public 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 int|string|bool The key/index of the element or FALSE if the element was not found. + */ + public function indexOf($element); + + /** + * Extracts 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 The offset to start from. + * @param int|null $length The maximum number of elements to return, or null for no limit. + * + * @return array + */ + public function slice($offset, $length = null); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php new file mode 100644 index 00000000000..3f9d43a8294 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php @@ -0,0 +1,259 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Expression; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Criteria for filtering Selectable collections. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Criteria +{ + /** + * @var string + */ + const ASC = 'ASC'; + + /** + * @var string + */ + const DESC = 'DESC'; + + /** + * @var \Doctrine\Common\Collections\ExpressionBuilder|null + */ + private static $expressionBuilder; + + /** + * @var \Doctrine\Common\Collections\Expr\Expression|null + */ + private $expression; + + /** + * @var string[] + */ + private $orderings = array(); + + /** + * @var int|null + */ + private $firstResult; + + /** + * @var int|null + */ + private $maxResults; + + /** + * Creates an instance of the class. + * + * @return Criteria + */ + public static function create() + { + return new static(); + } + + /** + * Returns the expression builder. + * + * @return \Doctrine\Common\Collections\ExpressionBuilder + */ + public static function expr() + { + if (self::$expressionBuilder === null) { + self::$expressionBuilder = new ExpressionBuilder(); + } + + return self::$expressionBuilder; + } + + /** + * Construct a new Criteria. + * + * @param Expression $expression + * @param string[]|null $orderings + * @param int|null $firstResult + * @param int|null $maxResults + */ + public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null) + { + $this->expression = $expression; + + $this->setFirstResult($firstResult); + $this->setMaxResults($maxResults); + + if (null !== $orderings) { + $this->orderBy($orderings); + } + } + + /** + * Sets the where expression to evaluate when this Criteria is searched for. + * + * @param Expression $expression + * + * @return Criteria + */ + public function where(Expression $expression) + { + $this->expression = $expression; + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an AND with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function andWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, array( + $this->expression, $expression + )); + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an OR with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function orWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, array( + $this->expression, $expression + )); + + return $this; + } + + /** + * Gets the expression attached to this Criteria. + * + * @return Expression|null + */ + public function getWhereExpression() + { + return $this->expression; + } + + /** + * Gets the current orderings of this Criteria. + * + * @return string[] + */ + public function getOrderings() + { + return $this->orderings; + } + + /** + * Sets the ordering of the result of this Criteria. + * + * Keys are field and values are the order, being either ASC or DESC. + * + * @see Criteria::ASC + * @see Criteria::DESC + * + * @param string[] $orderings + * + * @return Criteria + */ + public function orderBy(array $orderings) + { + $this->orderings = array_map( + function ($ordering) { + return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC; + }, + $orderings + ); + + return $this; + } + + /** + * Gets the current first result option of this Criteria. + * + * @return int|null + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Set the number of first result that this Criteria should return. + * + * @param int|null $firstResult The value to set. + * + * @return Criteria + */ + public function setFirstResult($firstResult) + { + $this->firstResult = null === $firstResult ? null : (int) $firstResult; + + return $this; + } + + /** + * Gets maxResults. + * + * @return int|null + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Sets maxResults. + * + * @param int|null $maxResults The value to set. + * + * @return Criteria + */ + public function setMaxResults($maxResults) + { + $this->maxResults = null === $maxResults ? null : (int) $maxResults; + + return $this; + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php new file mode 100644 index 00000000000..994085f914f --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php @@ -0,0 +1,227 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Walks an expression graph and turns it into a PHP closure. + * + * This closure can be used with {@Collection#filter()} and is used internally + * by {@ArrayCollection#select()}. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ClosureExpressionVisitor extends ExpressionVisitor +{ + /** + * Accesses the field of a given object. This field has to be public + * directly or indirectly (through an accessor get*, is*, or a magic + * method, __get, __call). + * + * @param object $object + * @param string $field + * + * @return mixed + */ + public static function getObjectFieldValue($object, $field) + { + if (is_array($object)) { + return $object[$field]; + } + + $accessors = array('get', 'is'); + + foreach ($accessors as $accessor) { + $accessor .= $field; + + if ( ! method_exists($object, $accessor)) { + continue; + } + + return $object->$accessor(); + } + + // __call should be triggered for get. + $accessor = $accessors[0] . $field; + + if (method_exists($object, '__call')) { + return $object->$accessor(); + } + + if ($object instanceof \ArrayAccess) { + return $object[$field]; + } + + return $object->$field; + } + + /** + * Helper for sorting arrays of objects based on multiple fields + orientations. + * + * @param string $name + * @param int $orientation + * @param \Closure $next + * + * @return \Closure + */ + public static function sortByField($name, $orientation = 1, \Closure $next = null) + { + if ( ! $next) { + $next = function() { + return 0; + }; + } + + return function ($a, $b) use ($name, $next, $orientation) { + $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); + $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); + + if ($aValue === $bValue) { + return $next($a, $b); + } + + return (($aValue > $bValue) ? 1 : -1) * $orientation; + }; + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + switch ($comparison->getOperator()) { + case Comparison::EQ: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; + }; + + case Comparison::NEQ: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; + }; + + case Comparison::LT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; + }; + + case Comparison::LTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; + }; + + case Comparison::GT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; + }; + + case Comparison::GTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; + }; + + case Comparison::IN: + return function ($object) use ($field, $value) { + return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::NIN: + return function ($object) use ($field, $value) { + return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::CONTAINS: + return function ($object) use ($field, $value) { + return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + default: + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return $this->andExpressions($expressionList); + + case CompositeExpression::TYPE_OR: + return $this->orExpressions($expressionList); + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * @param array $expressions + * + * @return callable + */ + private function andExpressions($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ( ! $expression($object)) { + return false; + } + } + return true; + }; + } + + /** + * @param array $expressions + * + * @return callable + */ + private function orExpressions($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ($expression($object)) { + return true; + } + } + return false; + }; + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php new file mode 100644 index 00000000000..d54ecf25c7d --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Comparison of a field with a value by the given operator. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Comparison implements Expression +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + const IS = '='; // no difference with EQ + const IN = 'IN'; + const NIN = 'NIN'; + const CONTAINS = 'CONTAINS'; + + /** + * @var string + */ + private $field; + + /** + * @var string + */ + private $op; + + /** + * @var Value + */ + private $value; + + /** + * @param string $field + * @param string $operator + * @param mixed $value + */ + public function __construct($field, $operator, $value) + { + if ( ! ($value instanceof Value)) { + $value = new Value($value); + } + + $this->field = $field; + $this->op = $operator; + $this->value = $value; + } + + /** + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * @return Value + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->op; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkComparison($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php new file mode 100644 index 00000000000..3613c027457 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression of Expressions combined by AND or OR operation. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class CompositeExpression implements Expression +{ + const TYPE_AND = 'AND'; + const TYPE_OR = 'OR'; + + /** + * @var string + */ + private $type; + + /** + * @var Expression[] + */ + private $expressions = array(); + + /** + * @param string $type + * @param array $expressions + * + * @throws \RuntimeException + */ + public function __construct($type, array $expressions) + { + $this->type = $type; + + foreach ($expressions as $expr) { + if ($expr instanceof Value) { + throw new \RuntimeException("Values are not supported expressions as children of and/or expressions."); + } + if ( ! ($expr instanceof Expression)) { + throw new \RuntimeException("No expression given to CompositeExpression."); + } + + $this->expressions[] = $expr; + } + } + + /** + * Returns the list of expressions nested in this composite. + * + * @return Expression[] + */ + public function getExpressionList() + { + return $this->expressions; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkCompositeExpression($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php new file mode 100644 index 00000000000..68db767c071 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression for the {@link Selectable} interface. + * + * @author Benjamin Eberlei + */ +interface Expression +{ + /** + * @param ExpressionVisitor $visitor + * + * @return mixed + */ + public function visit(ExpressionVisitor $visitor); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php new file mode 100644 index 00000000000..080afdc6df5 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * An Expression visitor walks a graph of expressions and turns them into a + * query for the underlying implementation. + * + * @author Benjamin Eberlei + */ +abstract class ExpressionVisitor +{ + /** + * Converts a comparison expression into the target query language output. + * + * @param Comparison $comparison + * + * @return mixed + */ + abstract public function walkComparison(Comparison $comparison); + + /** + * Converts a value expression into the target query language part. + * + * @param Value $value + * + * @return mixed + */ + abstract public function walkValue(Value $value); + + /** + * Converts a composite expression into the target query language output. + * + * @param CompositeExpression $expr + * + * @return mixed + */ + abstract public function walkCompositeExpression(CompositeExpression $expr); + + /** + * Dispatches walking an expression to the appropriate handler. + * + * @param Expression $expr + * + * @return mixed + * + * @throws \RuntimeException + */ + public function dispatch(Expression $expr) + { + switch (true) { + case ($expr instanceof Comparison): + return $this->walkComparison($expr); + + case ($expr instanceof Value): + return $this->walkValue($expr); + + case ($expr instanceof CompositeExpression): + return $this->walkCompositeExpression($expr); + + default: + throw new \RuntimeException("Unknown Expression " . get_class($expr)); + } + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php new file mode 100644 index 00000000000..7f6e83143b5 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +class Value implements Expression +{ + /** + * @var mixed + */ + private $value; + + /** + * @param mixed $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkValue($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php new file mode 100644 index 00000000000..6539e3c75fd --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php @@ -0,0 +1,166 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\Common\Collections\Expr\Value; + +/** + * Builder for Expressions in the {@link Selectable} interface. + * + * Important Notice for interoperable code: You have to use scalar + * values only for comparisons, otherwise the behavior of the comparision + * may be different between implementations (Array vs ORM vs ODM). + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ExpressionBuilder +{ + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function eq($field, $value) + { + return new Comparison($field, Comparison::EQ, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gt($field, $value) + { + return new Comparison($field, Comparison::GT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lt($field, $value) + { + return new Comparison($field, Comparison::LT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gte($field, $value) + { + return new Comparison($field, Comparison::GTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lte($field, $value) + { + return new Comparison($field, Comparison::LTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function neq($field, $value) + { + return new Comparison($field, Comparison::NEQ, new Value($value)); + } + + /** + * @param string $field + * + * @return Comparison + */ + public function isNull($field) + { + return new Comparison($field, Comparison::EQ, new Value(null)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function in($field, array $values) + { + return new Comparison($field, Comparison::IN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function notIn($field, array $values) + { + return new Comparison($field, Comparison::NIN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function contains($field, $value) + { + return new Comparison($field, Comparison::CONTAINS, new Value($value)); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php new file mode 100644 index 00000000000..57660ed7fcb --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common\Collections; + +/** + * Interface for collections that allow efficient filtering with an expression API. + * + * Goal of this interface is a backend independent method to fetch elements + * from a collections. {@link Expression} is crafted in a way that you can + * implement queries from both in-memory and database-backed collections. + * + * For database backed collections this allows very efficient access by + * utilizing the query APIs, for example SQL in the ORM. Applications using + * this API can implement efficient database access without having to ask the + * EntityManager or Repositories. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +interface Selectable +{ + /** + * Selects all elements from a selectable that match the expression and + * returns a new collection containing these elements. + * + * @param Criteria $criteria + * + * @return Collection + */ + public function matching(Criteria $criteria); +} diff --git a/vendor/doctrine/collections/phpunit.xml.dist b/vendor/doctrine/collections/phpunit.xml.dist new file mode 100644 index 00000000000..36968e99c30 --- /dev/null +++ b/vendor/doctrine/collections/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/AbstractLazyCollectionTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/AbstractLazyCollectionTest.php new file mode 100644 index 00000000000..4de82cc2037 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/AbstractLazyCollectionTest.php @@ -0,0 +1,20 @@ +assertFalse($collection->isInitialized()); + $this->assertCount(3, $collection); + + $collection->add('bar'); + $this->assertTrue($collection->isInitialized()); + $this->assertCount(4, $collection); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ArrayCollectionTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ArrayCollectionTest.php new file mode 100644 index 00000000000..1b8a906fdaf --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ArrayCollectionTest.php @@ -0,0 +1,296 @@ +. + */ + +namespace Doctrine\Tests\Common\Collections; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; + +/** + * Tests for {@see \Doctrine\Common\Collections\ArrayCollection} + * + * @covers \Doctrine\Common\Collections\ArrayCollection + */ +class ArrayCollectionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideDifferentElements + */ + public function testToArray($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame($elements, $collection->toArray()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testFirst($elements) + { + $collection = new ArrayCollection($elements); + $this->assertSame(reset($elements), $collection->first()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testLast($elements) + { + $collection = new ArrayCollection($elements); + $this->assertSame(end($elements), $collection->last()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testKey($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(key($elements), $collection->key()); + + next($elements); + $collection->next(); + + $this->assertSame(key($elements), $collection->key()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testNext($elements) + { + $collection = new ArrayCollection($elements); + + while (true) { + $collectionNext = $collection->next(); + $arrayNext = next($elements); + + if(!$collectionNext || !$arrayNext) { + break; + } + + $this->assertSame($arrayNext, $collectionNext, "Returned value of ArrayCollection::next() and next() not match"); + $this->assertSame(key($elements), $collection->key(), "Keys not match"); + $this->assertSame(current($elements), $collection->current(), "Current values not match"); + } + } + + /** + * @dataProvider provideDifferentElements + */ + public function testCurrent($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(current($elements), $collection->current()); + + next($elements); + $collection->next(); + + $this->assertSame(current($elements), $collection->current()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testGetKeys($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(array_keys($elements), $collection->getKeys()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testGetValues($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(array_values($elements), $collection->getValues()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testCount($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(count($elements), $collection->count()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testIterator($elements) + { + $collection = new ArrayCollection($elements); + + $iterations = 0; + foreach($collection->getIterator() as $key => $item) { + $this->assertSame($elements[$key], $item, "Item {$key} not match"); + $iterations++; + } + + $this->assertEquals(count($elements), $iterations, "Number of iterations not match"); + } + + /** + * @return array + */ + public function provideDifferentElements() + { + return array( + 'indexed' => array(array(1, 2, 3, 4, 5)), + 'associative' => array(array('A' => 'a', 'B' => 'b', 'C' => 'c')), + 'mixed' => array(array('A' => 'a', 1, 'B' => 'b', 2, 3)), + ); + } + + public function testRemove() + { + $elements = array(1, 'A' => 'a', 2, 'B' => 'b', 3); + $collection = new ArrayCollection($elements); + + $this->assertEquals(1, $collection->remove(0)); + unset($elements[0]); + + $this->assertEquals(null, $collection->remove('non-existent')); + unset($elements['non-existent']); + + $this->assertEquals(2, $collection->remove(1)); + unset($elements[1]); + + $this->assertEquals('a', $collection->remove('A')); + unset($elements['A']); + + $this->assertEquals($elements, $collection->toArray()); + } + + public function testRemoveElement() + { + $elements = array(1, 'A' => 'a', 2, 'B' => 'b', 3, 'A2' => 'a', 'B2' => 'b'); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->removeElement(1)); + unset($elements[0]); + + $this->assertFalse($collection->removeElement('non-existent')); + + $this->assertTrue($collection->removeElement('a')); + unset($elements['A']); + + $this->assertTrue($collection->removeElement('a')); + unset($elements['A2']); + + $this->assertEquals($elements, $collection->toArray()); + } + + public function testContainsKey() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'B2' => 'b'); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->containsKey(0), "Contains index 0"); + $this->assertTrue($collection->containsKey('A'), "Contains key \"A\""); + $this->assertTrue($collection->containsKey('null'), "Contains key \"null\", with value null"); + $this->assertFalse($collection->containsKey('non-existent'), "Doesn't contain key"); + } + + public function testEmpty() + { + $collection = new ArrayCollection(); + $this->assertTrue($collection->isEmpty(), "Empty collection"); + + $collection->add(1); + $this->assertFalse($collection->isEmpty(), "Not empty collection"); + } + + public function testContains() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->contains(0), "Contains Zero"); + $this->assertTrue($collection->contains('a'), "Contains \"a\""); + $this->assertTrue($collection->contains(null), "Contains Null"); + $this->assertFalse($collection->contains('non-existent'), "Doesn't contain an element"); + } + + public function testExists() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->exists(function($key, $element) { + return $key == 'A' && $element == 'a'; + }), "Element exists"); + + $this->assertFalse($collection->exists(function($key, $element) { + return $key == 'non-existent' && $element == 'non-existent'; + }), "Element not exists"); + } + + public function testIndexOf() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertSame(array_search(2, $elements, true), $collection->indexOf(2), 'Index of 2'); + $this->assertSame(array_search(null, $elements, true), $collection->indexOf(null), 'Index of null'); + $this->assertSame(array_search('non-existent', $elements, true), $collection->indexOf('non-existent'), 'Index of non existent'); + } + + public function testGet() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertSame(2, $collection->get(1), 'Get element by index'); + $this->assertSame('a', $collection->get('A'), 'Get element by name'); + $this->assertSame(null, $collection->get('non-existent'), 'Get non existent element'); + } + + public function testMatchingWithSortingPreservesyKeys() + { + $object1 = new \stdClass(); + $object2 = new \stdClass(); + + $object1->sortField = 2; + $object2->sortField = 1; + + $collection = new ArrayCollection(array( + 'object1' => $object1, + 'object2' => $object2, + )); + + $this->assertSame( + array( + 'object2' => $object2, + 'object1' => $object1, + ), + $collection + ->matching(new Criteria(null, array('sortField' => Criteria::ASC))) + ->toArray() + ); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php new file mode 100644 index 00000000000..21c2e3dfc52 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php @@ -0,0 +1,250 @@ +. + */ + +namespace Doctrine\Tests\Common\Collections; + +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; +use Doctrine\Common\Collections\ExpressionBuilder; + +/** + * @group DDC-1637 + */ +class ClosureExpressionVisitorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ClosureExpressionVisitor + */ + private $visitor; + + /** + * @var ExpressionBuilder + */ + private $builder; + + protected function setUp() + { + $this->visitor = new ClosureExpressionVisitor(); + $this->builder = new ExpressionBuilder(); + } + + public function testGetObjectFieldValueIsAccessor() + { + $object = new TestObject(1, 2, true); + + $this->assertTrue($this->visitor->getObjectFieldValue($object, 'baz')); + } + + public function testGetObjectFieldValueMagicCallMethod() + { + $object = new TestObject(1, 2, true, 3); + + $this->assertEquals(3, $this->visitor->getObjectFieldValue($object, 'qux')); + } + + public function testWalkEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->eq("foo", 1)); + + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(2))); + } + + public function testWalkNotEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->neq("foo", 1)); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(2))); + } + + public function testWalkLessThanComparison() + { + $closure = $this->visitor->walkComparison($this->builder->lt("foo", 1)); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(0))); + } + + public function testWalkLessThanEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->lte("foo", 1)); + + $this->assertFalse($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(0))); + } + + public function testWalkGreaterThanEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->gte("foo", 1)); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkGreaterThanComparison() + { + $closure = $this->visitor->walkComparison($this->builder->gt("foo", 1)); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertFalse($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkInComparison() + { + $closure = $this->visitor->walkComparison($this->builder->in("foo", array(1, 2, 3))); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkNotInComparison() + { + $closure = $this->visitor->walkComparison($this->builder->notIn("foo", array(1, 2, 3))); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(0))); + $this->assertTrue($closure(new TestObject(4))); + } + + public function testWalkContainsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->contains('foo', 'hello')); + + $this->assertTrue($closure(new TestObject('hello world'))); + $this->assertFalse($closure(new TestObject('world'))); + } + + public function testWalkAndCompositeExpression() + { + $closure = $this->visitor->walkCompositeExpression( + $this->builder->andX( + $this->builder->eq("foo", 1), + $this->builder->eq("bar", 1) + ) + ); + + $this->assertTrue($closure(new TestObject(1, 1))); + $this->assertFalse($closure(new TestObject(1, 0))); + $this->assertFalse($closure(new TestObject(0, 1))); + $this->assertFalse($closure(new TestObject(0, 0))); + } + + public function testWalkOrCompositeExpression() + { + $closure = $this->visitor->walkCompositeExpression( + $this->builder->orX( + $this->builder->eq("foo", 1), + $this->builder->eq("bar", 1) + ) + ); + + $this->assertTrue($closure(new TestObject(1, 1))); + $this->assertTrue($closure(new TestObject(1, 0))); + $this->assertTrue($closure(new TestObject(0, 1))); + $this->assertFalse($closure(new TestObject(0, 0))); + } + + public function testSortByFieldAscending() + { + $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); + $sort = ClosureExpressionVisitor::sortByField("foo"); + + usort($objects, $sort); + + $this->assertEquals("a", $objects[0]->getFoo()); + $this->assertEquals("b", $objects[1]->getFoo()); + $this->assertEquals("c", $objects[2]->getFoo()); + } + + public function testSortByFieldDescending() + { + $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); + $sort = ClosureExpressionVisitor::sortByField("foo", -1); + + usort($objects, $sort); + + $this->assertEquals("c", $objects[0]->getFoo()); + $this->assertEquals("b", $objects[1]->getFoo()); + $this->assertEquals("a", $objects[2]->getFoo()); + } + + public function testSortDelegate() + { + $objects = array(new TestObject("a", "c"), new TestObject("a", "b"), new TestObject("a", "a")); + $sort = ClosureExpressionVisitor::sortByField("bar", 1); + $sort = ClosureExpressionVisitor::sortByField("foo", 1, $sort); + + usort($objects, $sort); + + $this->assertEquals("a", $objects[0]->getBar()); + $this->assertEquals("b", $objects[1]->getBar()); + $this->assertEquals("c", $objects[2]->getBar()); + } + + public function testArrayComparison() + { + $closure = $this->visitor->walkComparison($this->builder->eq("foo", 42)); + + $this->assertTrue($closure(array('foo' => 42))); + } +} + +class TestObject +{ + private $foo; + private $bar; + private $baz; + private $qux; + + public function __construct($foo = null, $bar = null, $baz = null, $qux = null) + { + $this->foo = $foo; + $this->bar = $bar; + $this->baz = $baz; + $this->qux = $qux; + } + + public function __call($name, $arguments) + { + if ('getqux' === $name) { + return $this->qux; + } + } + + public function getFoo() + { + return $this->foo; + } + + public function getBar() + { + return $this->bar; + } + + public function isBaz() + { + return $this->baz; + } +} + diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php new file mode 100644 index 00000000000..f3f91ad3f13 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php @@ -0,0 +1,265 @@ +collection = new ArrayCollection(); + } + + public function testIssetAndUnset() + { + $this->assertFalse(isset($this->collection[0])); + $this->collection->add('testing'); + $this->assertTrue(isset($this->collection[0])); + unset($this->collection[0]); + $this->assertFalse(isset($this->collection[0])); + } + + public function testToString() + { + $this->collection->add('testing'); + $this->assertTrue(is_string((string) $this->collection)); + } + + public function testRemovingNonExistentEntryReturnsNull() + { + $this->assertEquals(null, $this->collection->remove('testing_does_not_exist')); + } + + public function testExists() + { + $this->collection->add("one"); + $this->collection->add("two"); + $exists = $this->collection->exists(function($k, $e) { return $e == "one"; }); + $this->assertTrue($exists); + $exists = $this->collection->exists(function($k, $e) { return $e == "other"; }); + $this->assertFalse($exists); + } + + public function testMap() + { + $this->collection->add(1); + $this->collection->add(2); + $res = $this->collection->map(function($e) { return $e * 2; }); + $this->assertEquals(array(2, 4), $res->toArray()); + } + + public function testFilter() + { + $this->collection->add(1); + $this->collection->add("foo"); + $this->collection->add(3); + $res = $this->collection->filter(function($e) { return is_numeric($e); }); + $this->assertEquals(array(0 => 1, 2 => 3), $res->toArray()); + } + + public function testFirstAndLast() + { + $this->collection->add('one'); + $this->collection->add('two'); + + $this->assertEquals($this->collection->first(), 'one'); + $this->assertEquals($this->collection->last(), 'two'); + } + + public function testArrayAccess() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + + $this->assertEquals($this->collection[0], 'one'); + $this->assertEquals($this->collection[1], 'two'); + + unset($this->collection[0]); + $this->assertEquals($this->collection->count(), 1); + } + + public function testContainsKey() + { + $this->collection[5] = 'five'; + $this->assertTrue($this->collection->containsKey(5)); + } + + public function testContains() + { + $this->collection[0] = 'test'; + $this->assertTrue($this->collection->contains('test')); + } + + public function testSearch() + { + $this->collection[0] = 'test'; + $this->assertEquals(0, $this->collection->indexOf('test')); + } + + public function testGet() + { + $this->collection[0] = 'test'; + $this->assertEquals('test', $this->collection->get(0)); + } + + public function testGetKeys() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals(array(0, 1), $this->collection->getKeys()); + } + + public function testGetValues() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals(array('one', 'two'), $this->collection->getValues()); + } + + public function testCount() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals($this->collection->count(), 2); + $this->assertEquals(count($this->collection), 2); + } + + public function testForAll() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals($this->collection->forAll(function($k, $e) { return is_string($e); }), true); + $this->assertEquals($this->collection->forAll(function($k, $e) { return is_array($e); }), false); + } + + public function testPartition() + { + $this->collection[] = true; + $this->collection[] = false; + $partition = $this->collection->partition(function($k, $e) { return $e == true; }); + $this->assertEquals($partition[0][0], true); + $this->assertEquals($partition[1][0], false); + } + + public function testClear() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->collection->clear(); + $this->assertEquals($this->collection->isEmpty(), true); + } + + public function testRemove() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $el = $this->collection->remove(0); + + $this->assertEquals('one', $el); + $this->assertEquals($this->collection->contains('one'), false); + $this->assertNull($this->collection->remove(0)); + } + + public function testRemoveElement() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + + $this->assertTrue($this->collection->removeElement('two')); + $this->assertFalse($this->collection->contains('two')); + $this->assertFalse($this->collection->removeElement('two')); + } + + public function testSlice() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->collection[] = 'three'; + + $slice = $this->collection->slice(0, 1); + $this->assertInternalType('array', $slice); + $this->assertEquals(array('one'), $slice); + + $slice = $this->collection->slice(1); + $this->assertEquals(array(1 => 'two', 2 => 'three'), $slice); + + $slice = $this->collection->slice(1, 1); + $this->assertEquals(array(1 => 'two'), $slice); + } + + public function fillMatchingFixture() + { + $std1 = new \stdClass(); + $std1->foo = "bar"; + $this->collection[] = $std1; + + $std2 = new \stdClass(); + $std2->foo = "baz"; + $this->collection[] = $std2; + } + + /** + * @group DDC-1637 + */ + public function testMatching() + { + $this->fillMatchingFixture(); + + $col = $this->collection->matching(new Criteria(Criteria::expr()->eq("foo", "bar"))); + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->collection); + $this->assertEquals(1, count($col)); + } + + /** + * @group DDC-1637 + */ + public function testMatchingOrdering() + { + $this->fillMatchingFixture(); + + $col = $this->collection->matching(new Criteria(null, array('foo' => 'DESC'))); + + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->collection); + $this->assertEquals(2, count($col)); + $this->assertEquals('baz', $col->first()->foo); + $this->assertEquals('bar', $col->last()->foo); + } + + /** + * @group DDC-1637 + */ + public function testMatchingSlice() + { + $this->fillMatchingFixture(); + + $col = $this->collection->matching(new Criteria(null, null, 1, 1)); + + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->collection); + $this->assertEquals(1, count($col)); + $this->assertEquals('baz', $col[0]->foo); + } + + public function testCanRemoveNullValuesByKey() + { + $this->collection->add(null); + $this->collection->remove(0); + $this->assertTrue($this->collection->isEmpty()); + } + + public function testCanVerifyExistingKeysWithNullValues() + { + $this->collection->set('key', null); + $this->assertTrue($this->collection->containsKey('key')); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php new file mode 100644 index 00000000000..1ab058a9b8c --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php @@ -0,0 +1,83 @@ +assertInstanceOf('Doctrine\Common\Collections\Criteria', $criteria); + } + + public function testConstructor() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria($expr, array("foo" => "ASC"), 10, 20); + + $this->assertSame($expr, $criteria->getWhereExpression()); + $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); + $this->assertEquals(10, $criteria->getFirstResult()); + $this->assertEquals(20, $criteria->getMaxResults()); + } + + public function testWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + + $this->assertSame($expr, $criteria->getWhereExpression()); + } + + public function testAndWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + $expr = $criteria->getWhereExpression(); + $criteria->andWhere($expr); + + $where = $criteria->getWhereExpression(); + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); + + $this->assertEquals(CompositeExpression::TYPE_AND, $where->getType()); + $this->assertSame(array($expr, $expr), $where->getExpressionList()); + } + + public function testOrWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + $expr = $criteria->getWhereExpression(); + $criteria->orWhere($expr); + + $where = $criteria->getWhereExpression(); + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); + + $this->assertEquals(CompositeExpression::TYPE_OR, $where->getType()); + $this->assertSame(array($expr, $expr), $where->getExpressionList()); + } + + public function testOrderings() + { + $criteria = Criteria::create() + ->orderBy(array("foo" => "ASC")); + + $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); + } + + public function testExpr() + { + $this->assertInstanceOf('Doctrine\Common\Collections\ExpressionBuilder', Criteria::expr()); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php new file mode 100644 index 00000000000..5f6b4ce2f01 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php @@ -0,0 +1,125 @@ +builder = new ExpressionBuilder(); + } + + public function testAndX() + { + $expr = $this->builder->andX($this->builder->eq("a", "b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $expr); + $this->assertEquals(CompositeExpression::TYPE_AND, $expr->getType()); + } + + public function testOrX() + { + $expr = $this->builder->orX($this->builder->eq("a", "b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $expr); + $this->assertEquals(CompositeExpression::TYPE_OR, $expr->getType()); + } + + public function testInvalidAndXArgument() + { + $this->setExpectedException("RuntimeException"); + $this->builder->andX("foo"); + } + + public function testEq() + { + $expr = $this->builder->eq("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::EQ, $expr->getOperator()); + } + + public function testNeq() + { + $expr = $this->builder->neq("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::NEQ, $expr->getOperator()); + } + + public function testLt() + { + $expr = $this->builder->lt("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::LT, $expr->getOperator()); + } + + public function testGt() + { + $expr = $this->builder->gt("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::GT, $expr->getOperator()); + } + + public function testGte() + { + $expr = $this->builder->gte("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::GTE, $expr->getOperator()); + } + + public function testLte() + { + $expr = $this->builder->lte("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::LTE, $expr->getOperator()); + } + + public function testIn() + { + $expr = $this->builder->in("a", array("b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::IN, $expr->getOperator()); + } + + public function testNotIn() + { + $expr = $this->builder->notIn("a", array("b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::NIN, $expr->getOperator()); + } + + public function testIsNull() + { + $expr = $this->builder->isNull("a"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::EQ, $expr->getOperator()); + } + + public function testContains() + { + $expr = $this->builder->contains("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::CONTAINS, $expr->getOperator()); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/LazyArrayCollection.php b/vendor/doctrine/collections/tests/Doctrine/Tests/LazyArrayCollection.php new file mode 100644 index 00000000000..56736b83b20 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/LazyArrayCollection.php @@ -0,0 +1,22 @@ +collection = new ArrayCollection(array('a', 'b', 'c')); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/TestInit.php b/vendor/doctrine/collections/tests/Doctrine/Tests/TestInit.php new file mode 100644 index 00000000000..973b5fef64f --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/TestInit.php @@ -0,0 +1,21 @@ +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 00000000000..1d93a131ed1 --- /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 00000000000..a12495ab1d3 --- /dev/null +++ b/vendor/doctrine/common/composer.json @@ -0,0 +1,36 @@ +{ + "name": "doctrine/common", + "type": "library", + "description": "Common Library for Doctrine projects", + "keywords": ["collections", "spl", "eventmanager", "annotations", "persistence"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "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.5|~7.0", + "doctrine/inflector": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/lexer": "1.*", + "doctrine/annotations": "1.*" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + } +} 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 00000000000..78eeb35d3a4 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php @@ -0,0 +1,280 @@ +. + */ + +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 + * + * @deprecated the ClassLoader is deprecated and will be removed in version 3.0 of doctrine/common. + */ +class ClassLoader +{ + /** + * PHP file extension. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Current namespace. + * + * @var string|null + */ + protected $namespace; + + /** + * Current include path. + * + * @var string|null + */ + protected $includePath; + + /** + * PHP namespace separator. + * + * @var string + */ + 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|null $ns The namespace of the classes to load. + * @param string|null $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. + * + * @return void + */ + 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|null $includePath + * + * @return void + */ + public function setIncludePath($includePath) + { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string|null + */ + public function getIncludePath() + { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + * + * @return void + */ + 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. + * + * @return void + */ + public function register() + { + spl_autoload_register([$this, 'loadClass']); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister([$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 (self::typeExists($className)) { + return true; + } + + if (! $this->canLoadClass($className)) { + return false; + } + + require ($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') + . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) + . $this->fileExtension; + + return self::typeExists($className); + } + + /** + * 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 is_file($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) + { + return self::typeExists($className, true); + } + + /** + * 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 ClassLoader 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) + && ($classLoader = reset($loader)) + && $classLoader instanceof ClassLoader + && $classLoader->canLoadClass($className) + ) { + return $classLoader; + } + } + + return null; + } + + /** + * Checks whether a given type exists + * + * @param string $type + * @param bool $autoload + * + * @return bool + */ + private static function typeExists($type, $autoload = false) + { + return class_exists($type, $autoload) + || interface_exists($type, $autoload) + || trait_exists($type, $autoload); + } +} 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 00000000000..2a1a08e7ef5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Base exception class for package Doctrine\Common. + * + * @author heinrich + */ +class CommonException extends \Exception +{ +} 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 00000000000..8cd02c9f293 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Comparable interface that allows to compare two value objects to each other for similarity. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +interface Comparable +{ + /** + * Compares 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. + * + * @param mixed $other + * + * @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 00000000000..75506e6acff --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php @@ -0,0 +1,67 @@ +. + */ + +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}. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventArgs +{ + /** + * Single instance of EventArgs. + * + * @var EventArgs + */ + 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 + * + * @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 00000000000..0ee04a15a46 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php @@ -0,0 +1,154 @@ +. + */ + +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. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventManager +{ + /** + * Map of registered listeners. + * => + * + * @var array + */ + private $_listeners = []; + + /** + * 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|null $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|null $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. + * + * @return void + */ + 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 + * + * @return void + */ + 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 it is + * interested in and added as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function addEventSubscriber(EventSubscriber $subscriber) + { + $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); + } + + /** + * Removes an EventSubscriber. The subscriber is asked for all the events it is + * interested in and removed as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function removeEventSubscriber(EventSubscriber $subscriber) + { + $this->removeEventListener($subscriber->getSubscribedEvents(), $subscriber); + } +} 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 00000000000..55d0f7d0ef3 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php @@ -0,0 +1,42 @@ +. + */ + +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. + * + * @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 + */ + public 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 00000000000..0aa07f85641 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common; + +use Doctrine\Common\Lexer\AbstractLexer; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * Lexer moved into its own Component Doctrine\Common\Lexer. This class + * only stays for being BC. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Lexer extends AbstractLexer +{ +} 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 00000000000..e25e9997081 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that provide the service of notifying listeners of + * changes to their properties. + * + * @link www.doctrine-project.org + * @since 2.0 + * @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 + * + * @return void + */ + public 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 00000000000..12de462943c --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php @@ -0,0 +1,262 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Abstract implementation of the ManagerRegistry contract. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +abstract class AbstractManagerRegistry implements ManagerRegistry +{ + /** + * @var string + */ + private $name; + + /** + * @var array + */ + private $connections; + + /** + * @var array + */ + private $managers; + + /** + * @var string + */ + private $defaultConnection; + + /** + * @var string + */ + private $defaultManager; + + /** + * @var string + */ + private $proxyInterfaceName; + + /** + * Constructor. + * + * @param string $name + * @param array $connections + * @param array $managers + * @param string $defaultConnection + * @param string $defaultManager + * @param string $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 The name of the service. + * + * @return object The 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 The name of the service. + * + * @return void + */ + abstract protected function resetService($name); + + /** + * Gets 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 = []; + 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} + * + * @throws \InvalidArgumentException + */ + 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) + { + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $proxyClass = new \ReflectionClass($class); + + if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { + if (! $parentClass = $proxyClass->getParentClass()) { + return null; + } + + $class = $parentClass->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 = []; + 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 00000000000..7c25e98aad0 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering connection for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @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. + */ + public function getDefaultConnectionName(); + + /** + * Gets the named connection. + * + * @param string $name The connection name (null for the default one). + * + * @return object + */ + public function getConnection($name = null); + + /** + * Gets an array of all registered connections. + * + * @return array An array of Connection instances. + */ + public function getConnections(); + + /** + * Gets all connection names. + * + * @return array An array of connection names. + */ + public 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 00000000000..52f41c0e751 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php @@ -0,0 +1,89 @@ +. + */ + +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 $object; + + /** + * Constructor. + * + * @param object $object + * @param ObjectManager $objectManager + */ + public function __construct($object, ObjectManager $objectManager) + { + $this->object = $object; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated entity. + * + * @deprecated + * + * @return object + */ + public function getEntity() + { + return $this->object; + } + + /** + * Retrieves the associated object. + * + * @return object + */ + public function getObject() + { + return $this->object; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} 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 00000000000..3d8abbe2383 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * 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 ClassMetadata $classMetadata + * @param ObjectManager $objectManager + */ + public function __construct(ClassMetadata $classMetadata, ObjectManager $objectManager) + { + $this->classMetadata = $classMetadata; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated ClassMetadata. + * + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * Retrieves the 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 00000000000..5527d4d47b8 --- /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\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Provides event arguments for the preFlush event. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ManagerEventArgs extends EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ObjectManager $objectManager + */ + public function __construct(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} 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 00000000000..b78bad99207 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Provides event arguments for the onClear event. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends EventArgs +{ + /** + * @var \Doctrine\Common\Persistence\ObjectManager + */ + private $objectManager; + + /** + * @var string|null + */ + private $entityClass; + + /** + * Constructor. + * + * @param ObjectManager $objectManager The object manager. + * @param string|null $entityClass The optional entity class. + */ + public function __construct($objectManager, $entityClass = null) + { + $this->objectManager = $objectManager; + $this->entityClass = $entityClass; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * Returns the name of the entity class that is cleared, or null if all are cleared. + * + * @return string|null + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Returns whether this event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} 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 00000000000..facce64710d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use 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; + } + + /** + * Retrieves the entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Checks if field has a changeset. + * + * @param string $field + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Gets 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]; + } + + /** + * Gets 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]; + } + + /** + * Sets the new value of this field. + * + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Asserts the field exists in changeset. + * + * @param string $field + * + * @return void + * + * @throws \InvalidArgumentException + */ + 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 00000000000..fce854b62c9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering object managers for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @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. + */ + public function getDefaultManagerName(); + + /** + * Gets a named object manager. + * + * @param string $name The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getManager($name = null); + + /** + * Gets an array of all registered object managers. + * + * @return \Doctrine\Common\Persistence\ObjectManager[] An array of ObjectManager instances + */ + public 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|null $name The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public 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. + */ + public function getAliasNamespace($alias); + + /** + * Gets all connection names. + * + * @return array An array of connection names. + */ + public 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 + */ + public 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 + */ + public 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 00000000000..586b2da2c08 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -0,0 +1,429 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Util\ClassUtils; +use ReflectionException; + +/** + * 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|null + */ + private $cacheDriver; + + /** + * @var ClassMetadata[] + */ + private $loadedMetadata = []; + + /** + * @var bool + */ + protected $initialized = false; + + /** + * @var ReflectionService|null + */ + private $reflectionService = null; + + /** + * Sets the cache driver used by the factory to cache ClassMetadata instances. + * + * @param \Doctrine\Common\Cache\Cache $cacheDriver + * + * @return void + */ + 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|null + */ + public function getCacheDriver() + { + return $this->cacheDriver; + } + + /** + * Returns an array of all the loaded metadata currently in memory. + * + * @return ClassMetadata[] + */ + 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 = []; + 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(); + + /** + * Gets the fully qualified class-name from the namespace alias. + * + * @param string $namespaceAlias + * @param string $simpleClassName + * + * @return string + */ + abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); + + /** + * Returns the mapping driver implementation. + * + * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver + */ + abstract protected function getDriver(); + + /** + * Wakes up reflection after ClassMetadata gets unserialized from cache. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * + * @return void + */ + abstract protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Initializes Reflection after ClassMetadata was constructed. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * + * @return void + */ + abstract protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Checks whether the class metadata is an entity. + * + * This method should return false for mapped superclasses or embedded classes. + * + * @param ClassMetadata $class + * + * @return boolean + */ + abstract protected function isEntity(ClassMetadata $class); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + * + * @throws ReflectionException + * @throws MappingException + */ + public function getMetadataFor($className) + { + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } + + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className, 2); + + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } else { + $realClassName = ClassUtils::getRealClass($className); + } + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + $loadingException = null; + + try { + 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); + } + } catch (MappingException $loadingException) { + if (! $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName)) { + throw $loadingException; + } + + $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; + } + + 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 + * + * @return void + */ + public function setMetadataFor($className, $class) + { + $this->loadedMetadata[$className] = $class; + } + + /** + * Gets an array of parent classes for the given entity class. + * + * @param string $name + * + * @return array + */ + protected function getParentClasses($name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = []; + 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. + * + * Important: The class $name does not necesarily exist at this point here. + * Scenarios in a code-generation setup might have access to XML/YAML + * Mapping files without the actual PHP code existing here. That is why the + * {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface + * should be used for reflection. + * + * @param string $name The name of the class for which the metadata should get loaded. + * + * @return array + */ + protected function loadMetadata($name) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $loaded = []; + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = []; + $reflService = $this->getReflectionService(); + foreach ($parentClasses as $className) { + if (isset($this->loadedMetadata[$className])) { + $parent = $this->loadedMetadata[$className]; + if ($this->isEntity($parent)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + continue; + } + + $class = $this->newClassMetadataInstance($className); + $this->initializeReflection($class, $reflService); + + $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); + + $this->loadedMetadata[$className] = $class; + + $parent = $class; + + if ($this->isEntity($class)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + + $this->wakeupReflection($class, $reflService); + + $loaded[] = $className; + } + + return $loaded; + } + + /** + * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions + * + * Override this method to implement a fallback strategy for failed metadata loading + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata|null + */ + protected function onNotFoundMetadata($className) + { + return null; + } + + /** + * Actually loads the metadata from the underlying metadata. + * + * @param ClassMetadata $class + * @param ClassMetadata|null $parent + * @param bool $rootEntityFound + * @param array $nonSuperclassParents All parent class names + * that are not marked as mapped superclasses. + * + * @return void + */ + abstract protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents); + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @param string $className + * + * @return ClassMetadata + */ + abstract protected function newClassMetadataInstance($className); + + /** + * {@inheritDoc} + */ + public function isTransient($class) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + + return $this->getDriver()->isTransient($class); + } + + /** + * Sets the reflectionService. + * + * @param ReflectionService $reflectionService + * + * @return void + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Gets 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 00000000000..b8445f1b9fc --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php @@ -0,0 +1,174 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadata +{ + /** + * Gets the fully-qualified class name of this persistent class. + * + * @return string + */ + public function getName(); + + /** + * Gets the mapped identifier field name. + * + * The returned structure is an array of the identifier field names. + * + * @return array + */ + public function getIdentifier(); + + /** + * Gets the ReflectionClass instance for this mapped class. + * + * @return \ReflectionClass + */ + public function getReflectionClass(); + + /** + * Checks if the given field name is a mapped identifier for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isIdentifier($fieldName); + + /** + * Checks if the given field is a mapped property for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function hasField($fieldName); + + /** + * Checks if the given field is a mapped association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function hasAssociation($fieldName); + + /** + * Checks if the given field is a mapped single valued association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isSingleValuedAssociation($fieldName); + + /** + * Checks if the given field is a mapped collection valued association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public 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 + */ + public function getFieldNames(); + + /** + * Returns an array of identifier field names numerically indexed. + * + * @return array + */ + public function getIdentifierFieldNames(); + + /** + * Returns 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(); + + /** + * 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 + */ + public function getTypeOfField($fieldName); + + /** + * Returns the target class name of the given association. + * + * @param string $assocName + * + * @return string + */ + public function getAssociationTargetClass($assocName); + + /** + * Checks if the association is the inverse side of a bidirectional association. + * + * @param string $assocName + * + * @return boolean + */ + public function isAssociationInverseSide($assocName); + + /** + * Returns the target field of the owning side of the association. + * + * @param string $assocName + * + * @return string + */ + public function getAssociationMappedByTargetField($assocName); + + /** + * Returns 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 + */ + public 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 00000000000..3d82881195b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @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. + */ + public function getAllMetadata(); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + */ + public 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. + */ + public function hasMetadataFor($className); + + /** + * Sets the metadata descriptor for a specific class. + * + * @param string $className + * + * @param ClassMetadata $class + */ + public function setMetadataFor($className, $class); + + /** + * Returns 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 + */ + public 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 00000000000..deb82c0c979 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,256 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader; +use 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 = []; + + /** + * The paths excluded from path where to look for mapping files. + * + * @var array + */ + protected $excludePaths = []; + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Cache for AnnotationDriver#getAllClassNames(). + * + * @var array|null + */ + protected $classNames; + + /** + * Name of the entity annotations as keys. + * + * @var array + */ + protected $entityAnnotationClasses = []; + + /** + * 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|null $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); + } + } + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Append exclude lookup paths to metadata driver. + * + * @param array $paths + */ + public function addExcludePaths(array $paths) + { + $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); + } + + /** + * Retrieve the defined metadata lookup exclude paths. + * + * @return array + */ + public function getExcludePaths() + { + return $this->excludePaths; + } + + /** + * Retrieve the current annotation reader + * + * @return AnnotationReader + */ + public function getReader() + { + return $this->reader; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets 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; + } + + /** + * Returns 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 = []; + $includedFiles = []; + + 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 + ), + '/^.+' . preg_quote($this->fileExtension) . '$/i', + \RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = $file[0]; + + if ( ! preg_match('(^phar:)i', $sourceFile)) { + $sourceFile = realpath($sourceFile); + } + + foreach ($this->excludePaths as $excludePath) { + $exclude = str_replace('\\', '/', realpath($excludePath)); + $current = str_replace('\\', '/', $sourceFile); + + if (strpos($current, $exclude) !== false) { + continue 2; + } + } + + 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 00000000000..6ed7f6d0d59 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -0,0 +1,173 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Locates the file that contains the metadata information for a given class name. + * + * This behavior is independent 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 = []; + + /** + * The file extension of mapping documents. + * + * @var string|null + */ + 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. + * @param string|null $fileExtension The file extension of mapping documents, usually prefixed with a dot. + */ + public function __construct($paths, $fileExtension = null) + { + $this->addPaths((array) $paths); + $this->fileExtension = $fileExtension; + } + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string|null + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string|null $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 (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename) + { + $classes = []; + + 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 (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return true; + } + } + + return false; + } +} 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 00000000000..dc799d5cc84 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php @@ -0,0 +1,214 @@ +. + */ + +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 '.'. + * + * @link www.doctrine-project.org + * @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|null + */ + protected $classCache; + + /** + * @var string|null + */ + 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 $locator A FileLocator or one/multiple paths + * where mapping documents can be found. + * @param string|null $fileExtension + */ + public function __construct($locator, $fileExtension = null) + { + if ($locator instanceof FileLocator) { + $this->locator = $locator; + } else { + $this->locator = new DefaultFileLocator((array)$locator, $fileExtension); + } + } + + /** + * Sets the global basename. + * + * @param string $file + * + * @return void + */ + public function setGlobalBasename($file) + { + $this->globalBasename = $file; + } + + /** + * Retrieves the global basename. + * + * @return string|null + */ + public function getGlobalBasename() + { + return $this->globalBasename; + } + + /** + * Gets 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. + * + * @param string $className + * + * @return array The element of schema meta data. + * + * @throws MappingException + */ + 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)); + if (!isset($result[$className])) { + throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->locator->getFileExtension()); + } + + return $result[$className]; + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return false; + } + + return !$this->locator->fileExists($className); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (! $this->classCache) { + return (array) $this->locator->getAllClassNames($this->globalBasename); + } + + return array_merge( + array_keys($this->classCache), + (array) $this->locator->getAllClassNames($this->globalBasename) + ); + } + + /** + * 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); + + /** + * Initializes 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 = []; + 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) + ); + } + } + } + } + + /** + * Retrieves the locator used to discover mapping files by className. + * + * @return FileLocator + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Sets the locator used to discover mapping files by className. + * + * @param FileLocator $locator + */ + public function setLocator(FileLocator $locator) + { + $this->locator = $locator; + } +} 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 00000000000..f622856da84 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +/** + * Locates the file that contains the metadata information for a given class name. + * + * This behavior is independent 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 +{ + /** + * Locates mapping file for the given class name. + * + * @param string $className + * + * @return string + */ + public function findMappingFile($className); + + /** + * Gets all class names that are found with this file locator. + * + * @param string $globalBasename Passed to allow excluding the basename. + * + * @return array + */ + public function getAllClassNames($globalBasename); + + /** + * Checks if a file can be found for this class name. + * + * @param string $className + * + * @return bool + */ + public function fileExists($className); + + /** + * Gets all the paths that this file locator looks for mapping files. + * + * @return array + */ + public function getPaths(); + + /** + * Gets the file extension that mapping files are suffixed with. + * + * @return string + */ + public 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 00000000000..b5d7ec03a1c --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php @@ -0,0 +1,58 @@ +. + */ + +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 + * + * @return void + */ + public 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. + */ + public function getAllClassNames(); + + /** + * Returns 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); +} 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 00000000000..50d9785bcd6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use 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 +{ + /** + * The default driver. + * + * @var MappingDriver|null + */ + private $defaultDriver; + + /** + * @var array + */ + private $drivers = []; + + /** + * Gets the default driver. + * + * @return MappingDriver|null + */ + public function getDefaultDriver() + { + return $this->defaultDriver; + } + + /** + * Set the default driver. + * + * @param MappingDriver $driver + * + * @return void + */ + public function setDefaultDriver(MappingDriver $driver) + { + $this->defaultDriver = $driver; + } + + /** + * Adds a nested driver. + * + * @param MappingDriver $nestedDriver + * @param string $namespace + * + * @return void + */ + public function addDriver(MappingDriver $nestedDriver, $namespace) + { + $this->drivers[$namespace] = $nestedDriver; + } + + /** + * Gets the array of nested drivers. + * + * @return array $drivers + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $driver MappingDriver */ + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + $driver->loadMetadataForClass($className, $metadata); + return; + } + } + + if (null !== $this->defaultDriver) { + $this->defaultDriver->loadMetadataForClass($className, $metadata); + return; + } + + throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $classNames = []; + $driverClasses = []; + + /* @var $driver MappingDriver */ + 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; + } + } + } + + if (null !== $this->defaultDriver) { + foreach ($this->defaultDriver->getAllClassNames() as $className) { + $classNames[$className] = true; + } + } + + return array_keys($classNames); + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + /* @var $driver MappingDriver */ + foreach ($this->drivers AS $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + return $driver->isTransient($className); + } + } + + if ($this->defaultDriver !== null) { + return $this->defaultDriver->isTransient($className); + } + + return true; + } +} 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 00000000000..efffa6aa4c7 --- /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. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PHPDriver extends FileDriver +{ + /** + * @var ClassMetadata + */ + protected $metadata; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = null) + { + parent::__construct($locator, '.php'); + } + + /** + * {@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; + + return [$metadata->getName() => $metadata]; + } +} 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 00000000000..8e37e5a7a16 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,142 @@ +. + */ + +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. + * + * @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 = []; + + /** + * Map of all class names. + * + * @var array + */ + private $classNames; + + /** + * Constructor. + * + * @param array|string $paths + */ + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + /** + * Adds paths. + * + * @param array $paths + * + * @return void + */ + 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 = []; + $includedFiles = []; + + 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 00000000000..4588cfec889 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -0,0 +1,239 @@ +. + */ + +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 = []; + + /** + * A map of mapping directory path to namespace prefix used to expand class shortnames. + * + * @var array + */ + protected $prefixes = []; + + /** + * File extension that is searched for. + * + * @var string|null + */ + protected $fileExtension; + + /** + * Represents PHP namespace delimiters when looking for files + * + * @var string + */ + private $nsSeparator; + + /** + * Constructor. + * + * @param array $prefixes + * @param string|null $fileExtension + * @param string $nsSeparator String which would be used when converting FQCN to filename and vice versa. Should not be empty + */ + public function __construct(array $prefixes, $fileExtension = null, $nsSeparator = '.') + { + $this->addNamespacePrefixes($prefixes); + $this->fileExtension = $fileExtension; + + if (empty($nsSeparator)) { + throw new \InvalidArgumentException('Namespace separator should not be empty'); + } + + $this->nsSeparator = (string) $nsSeparator; + } + + /** + * Adds Namespace Prefixes. + * + * @param array $prefixes + * + * @return void + */ + public function addNamespacePrefixes(array $prefixes) + { + $this->prefixes = array_merge($this->prefixes, $prefixes); + $this->paths = array_merge($this->paths, array_keys($prefixes)); + } + + /** + * Gets Namespace Prefixes. + * + * @return array + */ + public function getNamespacePrefixes() + { + return $this->prefixes; + } + + /** + * {@inheritDoc} + */ + public function getPaths() + { + return $this->paths; + } + + /** + * {@inheritDoc} + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets 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('\\', $this->nsSeparator, $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->nsSeparator).$this->fileExtension; + if (is_file($filename)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename = null) + { + $classes = []; + + 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])) { + + // Calculate namespace suffix for given prefix as a relative path from basepath to file path + $nsSuffix = strtr( + substr(realpath($file->getPath()), strlen(realpath($path))), + $this->nsSeparator, + '\\' + ); + + $classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' .str_replace($this->nsSeparator, '\\', $fileName); + } else { + $classes[] = str_replace($this->nsSeparator, '\\', $fileName); + } + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $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->nsSeparator ).$this->fileExtension; + if (is_file($filename)) { + return $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 00000000000..6e97485ebe2 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.2 + */ +class MappingException extends \Exception +{ + /** + * @param string $className + * @param array $namespaces + * + * @return self + */ + public static function classNotFoundInNamespaces($className, $namespaces) + { + return new self("The class '" . $className . "' was not found in the ". + "chain configured namespaces " . implode(", ", $namespaces)); + } + + /** + * @return self + */ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + /** + * @param string|null $path + * + * @return self + */ + 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!' + ); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return self + */ + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return self + */ + public static function invalidMappingFile($entityName, $fileName) + { + return new self("Invalid mapping file '$fileName' for class '$entityName'."); + } + + /** + * @param string $className + * + * @return self + */ + public static function nonExistingClass($className) + { + return new self("Class '$className' does not exist"); + } +} 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 00000000000..0088ed5ee25 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php @@ -0,0 +1,87 @@ +. + */ + +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 +{ + /** + * Returns an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * + * @throws \Doctrine\Common\Persistence\Mapping\MappingException + * + * @return array + */ + public function getParentClasses($class); + + /** + * Returns the shortname of a class. + * + * @param string $class + * + * @return string + */ + public function getClassShortName($class); + + /** + * @param string $class + * + * @return string + */ + public function getClassNamespace($class); + + /** + * Returns a reflection class instance or null. + * + * @param string $class + * + * @return \ReflectionClass|null + */ + public function getClass($class); + + /** + * Returns an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * + * @return \ReflectionProperty|null + */ + public function getAccessibleProperty($class, $property); + + /** + * Checks if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * + * @return bool + */ + public 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 00000000000..4598d5aff24 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Reflection\RuntimePublicReflectionProperty; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Service. + * + * @author Benjamin Eberlei + */ +class RuntimeReflectionService implements ReflectionService +{ + /** + * {@inheritDoc} + */ + public function getParentClasses($class) + { + if ( ! class_exists($class)) { + throw MappingException::nonExistingClass($class); + } + + return class_parents($class); + } + + /** + * {@inheritDoc} + */ + public function getClassShortName($class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getShortName(); + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace($class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getClass($class) + { + return new ReflectionClass($class); + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty($class, $property) + { + $reflectionProperty = new ReflectionProperty($class, $property); + + if ($reflectionProperty->isPublic()) { + $reflectionProperty = new RuntimePublicReflectionProperty($class, $property); + } + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod($class, $method) + { + try { + $reflectionMethod = new ReflectionMethod($class, $method); + } catch (ReflectionException $e) { + return false; + } + + return $reflectionMethod->isPublic(); + } +} 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 00000000000..7d017664924 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * PHP Runtime Reflection Service. + * + * @author Benjamin Eberlei + */ +class StaticReflectionService implements ReflectionService +{ + /** + * {@inheritDoc} + */ + public function getParentClasses($class) + { + return []; + } + + /** + * {@inheritDoc} + */ + public function getClassShortName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, "\\")+1); + } + return $className; + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace($className) + { + $namespace = ''; + if (strpos($className, '\\') !== false) { + $namespace = strrev(substr( strrev($className), strpos(strrev($className), '\\')+1 )); + } + return $namespace; + } + + /** + * {@inheritDoc} + */ + public function getClass($class) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty($class, $property) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod($class, $method) + { + return true; + } +} 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 00000000000..02208c9d596 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectManager class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectManager +{ + /** + * Finds an object by its identifier. + * + * This is just a convenient shortcut for getRepository($className)->find($id). + * + * @param string $className The class name of the object to find. + * @param mixed $id The identity of the object to find. + * + * @return object The found object. + */ + public 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. + * + * @return void + */ + public 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. + * + * @return void + */ + public 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 + * + * @return object + */ + public function merge($object); + + /** + * Clears the ObjectManager. All objects that are currently managed + * by this ObjectManager become detached. + * + * @param string|null $objectName if given, only objects of this type will get detached. + * + * @return void + */ + public function clear($objectName = null); + + /** + * 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. + * + * @return void + */ + public 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. + * + * @return void + */ + public 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. + * + * @return void + */ + public function flush(); + + /** + * Gets the repository for a class. + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + public 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 + */ + public function getClassMetadata($className); + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + public 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 + * + * @return void + */ + public function initializeObject($obj); + + /** + * Checks if the object is part of the current UnitOfWork and therefore managed. + * + * @param object $object + * + * @return bool + */ + public 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 00000000000..9bc248a56a3 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php @@ -0,0 +1,51 @@ +. + */ + +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 persistence-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 + * + * @return void + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php new file mode 100644 index 00000000000..8946475dbac --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php @@ -0,0 +1,140 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Base class to simplify ObjectManager decorators + * + * @license http://opensource.org/licenses/MIT MIT + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +abstract class ObjectManagerDecorator implements ObjectManager +{ + /** + * @var ObjectManager + */ + protected $wrapped; + + /** + * {@inheritdoc} + */ + public function find($className, $id) + { + return $this->wrapped->find($className, $id); + } + + /** + * {@inheritdoc} + */ + public function persist($object) + { + return $this->wrapped->persist($object); + } + + /** + * {@inheritdoc} + */ + public function remove($object) + { + return $this->wrapped->remove($object); + } + + /** + * {@inheritdoc} + */ + public function merge($object) + { + return $this->wrapped->merge($object); + } + + /** + * {@inheritdoc} + */ + public function clear($objectName = null) + { + return $this->wrapped->clear($objectName); + } + + /** + * {@inheritdoc} + */ + public function detach($object) + { + return $this->wrapped->detach($object); + } + + /** + * {@inheritdoc} + */ + public function refresh($object) + { + return $this->wrapped->refresh($object); + } + + /** + * {@inheritdoc} + */ + public function flush() + { + return $this->wrapped->flush(); + } + + /** + * {@inheritdoc} + */ + public function getRepository($className) + { + return $this->wrapped->getRepository($className); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata($className) + { + return $this->wrapped->getClassMetadata($className); + } + + /** + * {@inheritdoc} + */ + public function getMetadataFactory() + { + return $this->wrapped->getMetadataFactory(); + } + + /** + * {@inheritdoc} + */ + public function initializeObject($obj) + { + return $this->wrapped->initializeObject($obj); + } + + /** + * {@inheritdoc} + */ + public function contains($object) + { + return $this->wrapped->contains($object); + } +} 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 00000000000..f60721914a1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectRepository class to implement. + * + * @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 mixed $id The identifier. + * + * @return object The object. + */ + public function find($id); + + /** + * Finds all objects in the repository. + * + * @return array The objects. + */ + public 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. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array The objects. + * + * @throws \UnexpectedValueException + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null); + + /** + * Finds a single object by a set of criteria. + * + * @param array $criteria The criteria. + * + * @return object The object. + */ + public function findOneBy(array $criteria); + + /** + * Returns the class name of the object managed by the repository. + * + * @return string + */ + public 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 00000000000..990642e78e9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php @@ -0,0 +1,254 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * 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 as well. 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|null + */ + private static $objectManager = null; + + /** + * @var ClassMetadata|null + */ + private $cm = null; + + /** + * Sets the object manager responsible for all persistent object base classes. + * + * @param ObjectManager|null $objectManager + * + * @return void + */ + static public function setObjectManager(ObjectManager $objectManager = null) + { + self::$objectManager = $objectManager; + } + + /** + * @return ObjectManager|null + */ + static public function getObjectManager() + { + return self::$objectManager; + } + + /** + * Injects the Doctrine Object Manager. + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + * + * @return void + * + * @throws \RuntimeException + */ + 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. + * + * @param string $field + * @param array $args + * + * @return void + * + * @throws \BadMethodCallException When no persistent field exists by that name. + * @throws \InvalidArgumentException When the wrong target object type is passed to an association. + */ + 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()."'"); + } + } + + /** + * Gets a persistent field value. + * + * @param string $field + * + * @return mixed + * + * @throws \BadMethodCallException When no persistent field exists by that name. + */ + 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, completes the owning side. + * + * @param string $field + * @param ClassMetadata $targetClass + * @param object $targetObject + * + * @return void + */ + private function completeOwningSide($field, $targetClass, $targetObject) + { + // add this object on the owning side as well, 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); + } + } + + /** + * Adds an object to a collection. + * + * @param string $field + * @param array $args + * + * @return void + * + * @throws \BadMethodCallException + * @throws \InvalidArgumentException + */ + 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 ?: []); + } + $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()); + } + } + + /** + * Initializes Doctrine Metadata for this class. + * + * @return void + * + * @throws \RuntimeException + */ + 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 methods. + * + * @param string $method + * @param array $args + * + * @return mixed + * + * @throws \BadMethodCallException + */ + 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 00000000000..3369eb9498e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php @@ -0,0 +1,59 @@ +. + */ + +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 integer + */ + const MARKER_LENGTH = 6; + + /** + * Initializes this proxy if its not yet initialized. + * + * Acts as a no-op if already initialized. + * + * @return void + */ + public function __load(); + + /** + * Returns whether this proxy is 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 00000000000..1a59cd4dd8d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that are potential listeners of a NotifyPropertyChanged + * implementor. + * + * @link www.doctrine-project.org + * @since 2.0 + * @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. + * + * @return void + */ + function propertyChanged($sender, $propertyName, $oldValue, $newValue); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php new file mode 100644 index 00000000000..7eb216bfc57 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php @@ -0,0 +1,248 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\OutOfBoundsException; +use Doctrine\Common\Util\ClassUtils; + +/** + * Abstract factory for proxy objects. + * + * @author Benjamin Eberlei + */ +abstract class AbstractProxyFactory +{ + /** + * Never autogenerate a proxy and rely that it was generated by some + * process before deployment. + * + * @var integer + */ + const AUTOGENERATE_NEVER = 0; + + /** + * Always generates a new proxy in every request. + * + * This is only sane during development. + * + * @var integer + */ + const AUTOGENERATE_ALWAYS = 1; + + /** + * Autogenerate the proxy class when the proxy file does not exist. + * + * This strategy causes a file exists call whenever any proxy is used the + * first time in a request. + * + * @var integer + */ + const AUTOGENERATE_FILE_NOT_EXISTS = 2; + + /** + * Generate the proxy classes using eval(). + * + * This strategy is only sane for development, and even then it gives me + * the creeps a little. + * + * @var integer + */ + const AUTOGENERATE_EVAL = 3; + + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * @var \Doctrine\Common\Proxy\ProxyGenerator the proxy generator responsible for creating the proxy classes/files. + */ + private $proxyGenerator; + + /** + * @var bool Whether to automatically (re)generate proxy classes. + */ + private $autoGenerate; + + /** + * @var \Doctrine\Common\Proxy\ProxyDefinition[] + */ + private $definitions = []; + + /** + * @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory + * @param bool|int $autoGenerate + */ + public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) + { + $this->proxyGenerator = $proxyGenerator; + $this->metadataFactory = $metadataFactory; + $this->autoGenerate = (int)$autoGenerate; + } + + /** + * Gets a reference proxy instance for the entity of the given type and identified by + * the given identifier. + * + * @param string $className + * @param array $identifier + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\OutOfBoundsException + */ + public function getProxy($className, array $identifier) + { + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + $fqcn = $definition->proxyClassName; + $proxy = new $fqcn($definition->initializer, $definition->cloner); + + foreach ($definition->identifierFields as $idField) { + if (! isset($identifier[$idField])) { + throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField); + } + + $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]); + } + + return $proxy; + } + + /** + * Generates proxy classes for all given classes. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata[] $classes The classes (ClassMetadata instances) + * for which to generate proxies. + * @param string $proxyDir 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, $proxyDir = null) + { + $generated = 0; + + foreach ($classes as $class) { + if ($this->skipClass($class)) { + continue; + } + + $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir); + + $this->proxyGenerator->generateProxyClass($class, $proxyFileName); + + $generated += 1; + } + + return $generated; + } + + /** + * Reset initialization/cloning logic for an un-initialized proxy + * + * @param \Doctrine\Common\Proxy\Proxy $proxy + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException + */ + public function resetUninitializedProxy(Proxy $proxy) + { + if ($proxy->__isInitialized()) { + throw InvalidArgumentException::unitializedProxyExpected($proxy); + } + + $className = ClassUtils::getClass($proxy); + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + + $proxy->__setInitializer($definition->initializer); + $proxy->__setCloner($definition->cloner); + + return $proxy; + } + + /** + * Get a proxy definition for the given class name. + * + * @param string $className + * + * @return ProxyDefinition + */ + private function getProxyDefinition($className) + { + $classMetadata = $this->metadataFactory->getMetadataFor($className); + $className = $classMetadata->getName(); // aliases and case sensitivity + + $this->definitions[$className] = $this->createProxyDefinition($className); + $proxyClassName = $this->definitions[$className]->proxyClassName; + + if ( ! class_exists($proxyClassName, false)) { + $fileName = $this->proxyGenerator->getProxyFileName($className); + + switch ($this->autoGenerate) { + case self::AUTOGENERATE_NEVER: + require $fileName; + break; + + case self::AUTOGENERATE_FILE_NOT_EXISTS: + if ( ! file_exists($fileName)) { + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + } + require $fileName; + break; + + case self::AUTOGENERATE_ALWAYS: + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + require $fileName; + break; + + case self::AUTOGENERATE_EVAL: + $this->proxyGenerator->generateProxyClass($classMetadata, false); + break; + } + } + + return $this->definitions[$className]; + } + + /** + * Determine if this class should be skipped during proxy generation. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $metadata + * + * @return bool + */ + abstract protected function skipClass(ClassMetadata $metadata); + + /** + * @param string $className + * + * @return ProxyDefinition + */ + abstract protected function createProxyDefinition($className); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php new file mode 100644 index 00000000000..0aa930b229b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; + +/** + * Special Autoloader for Proxy classes, which are not PSR-0 compliant. + * + * @author Benjamin Eberlei + */ +class Autoloader +{ + /** + * Resolves proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name. + * 2. Remove namespace separators 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 + * + * @throws InvalidArgumentException + */ + public static function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (0 !== strpos($className, $proxyNamespace)) { + throw InvalidArgumentException::notProxyClass($className, $proxyNamespace); + } + + $className = str_replace('\\', '', substr($className, strlen($proxyNamespace) + 1)); + + return $proxyDir . DIRECTORY_SEPARATOR . $className . '.php'; + } + + /** + * Registers and returns autoloader callback for the given proxy dir and namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param callable|null $notFoundCallback Invoked when the proxy file is not found. + * + * @return \Closure + * + * @throws InvalidArgumentException + */ + public static function register($proxyDir, $proxyNamespace, $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, '\\'); + + if ( ! (null === $notFoundCallback || is_callable($notFoundCallback))) { + throw InvalidArgumentException::invalidClassNotFoundCallback($notFoundCallback); + } + + $autoloader = function ($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if (0 === strpos($className, $proxyNamespace)) { + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + call_user_func($notFoundCallback, $proxyDir, $proxyNamespace, $className); + } + + require $file; + } + }; + + spl_autoload_register($autoloader); + + return $autoloader; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php new file mode 100644 index 00000000000..dff54d9fd2f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use Doctrine\Common\Persistence\Proxy; +use InvalidArgumentException as BaseInvalidArgumentException; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ProxyException +{ + /** + * @return self + */ + public static function proxyDirectoryRequired() + { + return new self('You must configure a proxy directory. See docs for details'); + } + + /** + * @param string $className + * @param string $proxyNamespace + * + * @return self + */ + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf('The class "%s" is not part of the proxy namespace "%s"', $className, $proxyNamespace)); + } + + /** + * @param string $name + * + * @return self + */ + public static function invalidPlaceholder($name) + { + return new self(sprintf('Provided placeholder for "%s" must be either a string or a valid callable', $name)); + } + + /** + * @return self + */ + public static function proxyNamespaceRequired() + { + return new self('You must configure a proxy namespace'); + } + + /** + * @param Proxy $proxy + * + * @return self + */ + public static function unitializedProxyExpected(Proxy $proxy) + { + return new self(sprintf('Provided proxy of type "%s" must not be initialized.', get_class($proxy))); + } + + /** + * @param mixed $callback + * + * @return self + */ + public static function invalidClassNotFoundCallback($callback) + { + $type = is_object($callback) ? get_class($callback) : gettype($callback); + + return new self(sprintf('Invalid \$notFoundCallback given: must be a callable, "%s" given', $type)); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php new file mode 100644 index 00000000000..3c7415c6578 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use OutOfBoundsException as BaseOutOfBoundsException; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + * @author Fredrik Wendel + */ +class OutOfBoundsException extends BaseOutOfBoundsException implements ProxyException +{ + /** + * @param string $className + * @param string $idField + * + * @return self + */ + public static function missingPrimaryKeyValue($className, $idField) + { + return new self(sprintf("Missing value for primary key %s on %s", $idField, $className)); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php new file mode 100644 index 00000000000..0d1ff1408f9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +/** + * Base exception interface for proxy exceptions. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +interface ProxyException +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php new file mode 100644 index 00000000000..154b9177995 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Proxy Unexpected Value Exception. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ProxyException +{ + /** + * @param string $proxyDirectory + * + * @return self + */ + public static function proxyDirectoryNotWritable($proxyDirectory) + { + return new self(sprintf('Your proxy directory "%s" must be writable', $proxyDirectory)); + } + + /** + * @param string $className + * @param string $methodName + * @param string $parameterName + * @param \Exception $previous + * + * @return self + */ + public static function invalidParameterTypeHint($className, $methodName, $parameterName, \Exception $previous) + { + return new self( + sprintf( + 'The type hint of parameter "%s" in method "%s" in class "%s" is invalid.', + $parameterName, + $methodName, + $className + ), + 0, + $previous + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php new file mode 100644 index 00000000000..bd2b0887478 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Closure; +use Doctrine\Common\Persistence\Proxy as BaseProxy; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @author Marco Pivetta + * @since 2.4 + */ +interface Proxy extends BaseProxy +{ + /** + * Marks the proxy as initialized or not. + * + * @param boolean $initialized + * + * @return void + */ + public function __setInitialized($initialized); + + /** + * Sets the initializer callback to be used when initializing the proxy. That + * initializer should accept 3 parameters: $proxy, $method and $params. Those + * are respectively the proxy object that is being initialized, the method name + * that triggered initialization and the parameters passed to that method. + * + * @param Closure|null $initializer + * + * @return void + */ + public function __setInitializer(Closure $initializer = null); + + /** + * Retrieves the initializer callback used to initialize the proxy. + * + * @see __setInitializer + * + * @return Closure|null + */ + public function __getInitializer(); + + /** + * Sets the callback to be used when cloning the proxy. That initializer should accept + * a single parameter, which is the cloned proxy instance itself. + * + * @param Closure|null $cloner + * + * @return void + */ + public function __setCloner(Closure $cloner = null); + + /** + * Retrieves the callback to be used when cloning the proxy. + * + * @see __setCloner + * + * @return Closure|null + */ + public function __getCloner(); + + /** + * Retrieves the list of lazy loaded properties for a given proxy + * + * @return array Keys are the property names, and values are the default values + * for those properties. + */ + public function __getLazyProperties(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php new file mode 100644 index 00000000000..48b149a305e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +/** + * Definition structure how to create a proxy. + * + * @author Benjamin Eberlei + */ +class ProxyDefinition +{ + /** + * @var string + */ + public $proxyClassName; + + /** + * @var array + */ + public $identifierFields; + + /** + * @var \ReflectionProperty[] + */ + public $reflectionFields; + + /** + * @var callable + */ + public $initializer; + + /** + * @var callable + */ + public $cloner; + + /** + * @param string $proxyClassName + * @param array $identifierFields + * @param array $reflectionFields + * @param callable $initializer + * @param callable $cloner + */ + public function __construct($proxyClassName, array $identifierFields, array $reflectionFields, $initializer, $cloner) + { + $this->proxyClassName = $proxyClassName; + $this->identifierFields = $identifierFields; + $this->reflectionFields = $reflectionFields; + $this->initializer = $initializer; + $this->cloner = $cloner; + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php new file mode 100644 index 00000000000..925dcd1b7bc --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -0,0 +1,1037 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\Common\Util\ClassUtils; + +/** + * This factory is used to generate proxy classes. + * It builds proxies from given parameters, a template and class metadata. + * + * @author Marco Pivetta + * @since 2.4 + */ +class ProxyGenerator +{ + /** + * Used to match very simple id methods that don't need + * to be decorated since the identifier is known. + */ + const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*{\s*return\s*\$this->%s;\s*})i'; + + /** + * The namespace that contains all proxy classes. + * + * @var string + */ + private $proxyNamespace; + + /** + * The directory that contains all proxy classes. + * + * @var string + */ + private $proxyDirectory; + + /** + * Map of callables used to fill in placeholders set in the template. + * + * @var string[]|callable[] + */ + protected $placeholders = [ + 'baseProxyInterface' => Proxy::class, + 'additionalProperties' => '', + ]; + + /** + * Template used as a blueprint to generate proxies. + * + * @var string + */ + protected $proxyClassTemplate = '; + +/** + * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR + */ +class extends \ implements \ +{ + /** + * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with + * three parameters, being respectively the proxy object to be initialized, the method that triggered the + * initialization process and an array of ordered parameters that were passed to that method. + * + * @see \Doctrine\Common\Persistence\Proxy::__setInitializer + */ + public $__initializer__; + + /** + * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object + * + * @see \Doctrine\Common\Persistence\Proxy::__setCloner + */ + public $__cloner__; + + /** + * @var boolean flag indicating if this object was already initialized + * + * @see \Doctrine\Common\Persistence\Proxy::__isInitialized + */ + public $__isInitialized__ = false; + + /** + * @var array properties to be lazy loaded, with keys being the property + * names and values being their default values + * + * @see \Doctrine\Common\Persistence\Proxy::__getLazyProperties + */ + public static $lazyPropertiesDefaults = []; + + + + + + + + + + + + + + + + + + /** + * Forces initialization of the proxy + */ + public function __load() + { + $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []); + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitialized($initialized) + { + $this->__isInitialized__ = $initialized; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitializer(\Closure $initializer = null) + { + $this->__initializer__ = $initializer; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __getInitializer() + { + return $this->__initializer__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setCloner(\Closure $cloner = null) + { + $this->__cloner__ = $cloner; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific cloning logic + */ + public function __getCloner() + { + return $this->__cloner__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + * @static + */ + public function __getLazyProperties() + { + return self::$lazyPropertiesDefaults; + } + + +} +'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param string $proxyDirectory The directory to use for the proxy classes. It must exist. + * @param string $proxyNamespace The namespace to use for the proxy classes. + * + * @throws InvalidArgumentException + */ + public function __construct($proxyDirectory, $proxyNamespace) + { + if ( ! $proxyDirectory) { + throw InvalidArgumentException::proxyDirectoryRequired(); + } + + if ( ! $proxyNamespace) { + throw InvalidArgumentException::proxyNamespaceRequired(); + } + + $this->proxyDirectory = $proxyDirectory; + $this->proxyNamespace = $proxyNamespace; + } + + /** + * Sets a placeholder to be replaced in the template. + * + * @param string $name + * @param string|callable $placeholder + * + * @throws InvalidArgumentException + */ + public function setPlaceholder($name, $placeholder) + { + if ( ! is_string($placeholder) && ! is_callable($placeholder)) { + throw InvalidArgumentException::invalidPlaceholder($name); + } + + $this->placeholders[$name] = $placeholder; + } + + /** + * Sets the base template used to create proxy classes. + * + * @param string $proxyClassTemplate + */ + public function setProxyClassTemplate($proxyClassTemplate) + { + $this->proxyClassTemplate = (string) $proxyClassTemplate; + } + + /** + * Generates a proxy class file. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class Metadata for the original class. + * @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used. + * + * @throws UnexpectedValueException + */ + public function generateProxyClass(ClassMetadata $class, $fileName = false) + { + preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); + + $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]); + $placeholders = []; + + foreach ($placeholderMatches as $placeholder => $name) { + $placeholders[$placeholder] = isset($this->placeholders[$name]) + ? $this->placeholders[$name] + : [$this, 'generate' . $name]; + } + + foreach ($placeholders as & $placeholder) { + if (is_callable($placeholder)) { + $placeholder = call_user_func($placeholder, $class); + } + } + + $proxyCode = strtr($this->proxyClassTemplate, $placeholders); + + if ( ! $fileName) { + $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class); + + if ( ! class_exists($proxyClassName)) { + eval(substr($proxyCode, 5)); + } + + return; + } + + $parentDirectory = dirname($fileName); + + if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + if ( ! is_writable($parentDirectory)) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + $tmpFileName = $fileName . '.' . uniqid('', true); + + file_put_contents($tmpFileName, $proxyCode); + @chmod($tmpFileName, 0664); + rename($tmpFileName, $fileName); + } + + /** + * Generates the proxy short class name to be used in the template. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateProxyShortClassName(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[0]); + } + + /** + * Generates the proxy namespace. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateNamespace(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[1]); + } + + /** + * Generates the original class name. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateClassName(ClassMetadata $class) + { + return ltrim($class->getName(), '\\'); + } + + /** + * Generates the array representation of lazy loaded public properties and their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateLazyPropertiesDefaults(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $values = []; + + foreach ($lazyPublicProperties as $key => $value) { + $values[] = var_export($key, true) . ' => ' . var_export($value, true); + } + + return implode(', ', $values); + } + + /** + * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateConstructorImpl(ClassMetadata $class) + { + $constructorImpl = <<<'EOT' + /** + * @param \Closure $initializer + * @param \Closure $cloner + */ + public function __construct($initializer = null, $cloner = null) + { + +EOT; + $toUnset = []; + + foreach ($this->getLazyLoadedPublicProperties($class) as $lazyPublicProperty => $unused) { + $toUnset[] = '$this->' . $lazyPublicProperty; + } + + $constructorImpl .= (empty($toUnset) ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") + . <<<'EOT' + + $this->__initializer__ = $initializer; + $this->__cloner__ = $cloner; + } +EOT; + + return $constructorImpl; + } + + /** + * Generates the magic getter invoked when lazy loaded public properties are requested. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicGet(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $reflectionClass = $class->getReflectionClass(); + $hasParentGet = false; + $returnReference = ''; + $inheritDoc = ''; + + if ($reflectionClass->hasMethod('__get')) { + $hasParentGet = true; + $inheritDoc = '{@inheritDoc}'; + + if ($reflectionClass->getMethod('__get')->returnsReference()) { + $returnReference = '& '; + } + } + + if (empty($lazyPublicProperties) && ! $hasParentGet) { + return ''; + } + + $magicGet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return $this->$name; + } + + +EOT; + } + + if ($hasParentGet) { + $magicGet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return parent::__get($name); + +EOT; + } else { + $magicGet .= <<<'EOT' + trigger_error(sprintf('Undefined property: %s::$%s', __CLASS__, $name), E_USER_NOTICE); + +EOT; + } + + $magicGet .= " }"; + + return $magicGet; + } + + /** + * Generates the magic setter (currently unused). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicSet(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $hasParentSet = $class->getReflectionClass()->hasMethod('__set'); + + if (empty($lazyPublicProperties) && ! $hasParentSet) { + return ''; + } + + $inheritDoc = $hasParentSet ? '{@inheritDoc}' : ''; + $magicSet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + $this->$name = $value; + + return; + } + + +EOT; + } + + if ($hasParentSet) { + $magicSet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + return parent::__set($name, $value); +EOT; + } else { + $magicSet .= " \$this->\$name = \$value;"; + } + + $magicSet .= "\n }"; + + return $magicSet; + } + + /** + * Generates the magic issetter invoked when lazy loaded public properties are checked against isset(). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicIsset(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset'); + + if (empty($lazyPublicProperties) && ! $hasParentIsset) { + return ''; + } + + $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : ''; + $magicIsset = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return isset($this->$name); + } + + +EOT; + } + + if ($hasParentIsset) { + $magicIsset .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return parent::__isset($name); + +EOT; + } else { + $magicIsset .= " return false;"; + } + + return $magicIsset . "\n }"; + } + + /** + * Generates implementation for the `__sleep` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateSleepImpl(ClassMetadata $class) + { + $hasParentSleep = $class->getReflectionClass()->hasMethod('__sleep'); + $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : ''; + $sleepImpl = <<__isInitialized__) { + $properties = array_diff($properties, array_keys($this->__getLazyProperties())); + } + + return $properties; + } +EOT; + } + + $allProperties = ['__isInitialized__']; + + /* @var $prop \ReflectionProperty */ + foreach ($class->getReflectionClass()->getProperties() as $prop) { + if ($prop->isStatic()) { + continue; + } + + $allProperties[] = $prop->isPrivate() + ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName() + : $prop->getName(); + } + + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $protectedProperties = array_diff($allProperties, $lazyPublicProperties); + + foreach ($allProperties as &$property) { + $property = var_export($property, true); + } + + foreach ($protectedProperties as &$property) { + $property = var_export($property, true); + } + + $allProperties = implode(', ', $allProperties); + $protectedProperties = implode(', ', $protectedProperties); + + return $sleepImpl . <<__isInitialized__) { + return [$allProperties]; + } + + return [$protectedProperties]; + } +EOT; + } + + /** + * Generates implementation for the `__wakeup` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateWakeupImpl(ClassMetadata $class) + { + $unsetPublicProperties = []; + $hasWakeup = $class->getReflectionClass()->hasMethod('__wakeup'); + + foreach (array_keys($this->getLazyLoadedPublicProperties($class)) as $lazyPublicProperty) { + $unsetPublicProperties[] = '$this->' . $lazyPublicProperty; + } + + $shortName = $this->generateProxyShortClassName($class); + $inheritDoc = $hasWakeup ? '{@inheritDoc}' : ''; + $wakeupImpl = <<__isInitialized__) { + \$this->__initializer__ = function ($shortName \$proxy) { + \$proxy->__setInitializer(null); + \$proxy->__setCloner(null); + + \$existingProperties = get_object_vars(\$proxy); + + foreach (\$proxy->__getLazyProperties() as \$property => \$defaultValue) { + if ( ! array_key_exists(\$property, \$existingProperties)) { + \$proxy->\$property = \$defaultValue; + } + } + }; + +EOT; + + if ( ! empty($unsetPublicProperties)) { + $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ");"; + } + + $wakeupImpl .= "\n }"; + + if ($hasWakeup) { + $wakeupImpl .= "\n parent::__wakeup();"; + } + + $wakeupImpl .= "\n }"; + + return $wakeupImpl; + } + + /** + * Generates implementation for the `__clone` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateCloneImpl(ClassMetadata $class) + { + $hasParentClone = $class->getReflectionClass()->hasMethod('__clone'); + $inheritDoc = $hasParentClone ? '{@inheritDoc}' : ''; + $callParentClone = $hasParentClone ? "\n parent::__clone();\n" : ''; + + return <<__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []); +$callParentClone } +EOT; + } + + /** + * Generates decorated methods by picking those available in the parent class. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMethods(ClassMetadata $class) + { + $methods = ''; + $methodNames = []; + $reflectionMethods = $class->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC); + $skippedMethods = [ + '__sleep' => true, + '__clone' => true, + '__wakeup' => true, + '__get' => true, + '__set' => true, + '__isset' => true, + ]; + + foreach ($reflectionMethods as $method) { + $name = $method->getName(); + + if ( + $method->isConstructor() || + isset($skippedMethods[strtolower($name)]) || + isset($methodNames[$name]) || + $method->isFinal() || + $method->isStatic() || + ( ! $method->isPublic()) + ) { + continue; + } + + $methodNames[$name] = true; + $methods .= "\n /**\n" + . " * {@inheritDoc}\n" + . " */\n" + . ' public function '; + + if ($method->returnsReference()) { + $methods .= '&'; + } + + $methods .= $name . '(' . $this->buildParametersString($class, $method, $method->getParameters()) . ')'; + $methods .= $this->getMethodReturnType($method); + $methods .= "\n" . ' {' . "\n"; + + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($name, 3)); + $fieldType = $class->getTypeOfField($identifier); + $cast = in_array($fieldType, ['integer', 'smallint']) ? '(int) ' : ''; + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' return ' . $cast . ' parent::' . $method->getName() . "();\n"; + $methods .= ' }' . "\n\n"; + } + + $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters())); + $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters())); + + $methods .= "\n \$this->__initializer__ " + . "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true) + . ", [" . $invokeParamsString . "]);" + . "\n\n return parent::" . $name . '(' . $callParamsString . ');' + . "\n" . ' }' . "\n"; + } + + return $methods; + } + + /** + * Generates the Proxy file name. + * + * @param string $className + * @param string $baseDirectory 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 + */ + public function getProxyFileName($className, $baseDirectory = null) + { + $baseDirectory = $baseDirectory ?: $this->proxyDirectory; + + return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER + . str_replace('\\', '', $className) . '.php'; + } + + /** + * Checks 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 \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return boolean + */ + private function isShortIdentifierGetter($method, ClassMetadata $class) + { + $identifier = lcfirst(substr($method->getName(), 3)); + $startLine = $method->getStartLine(); + $endLine = $method->getEndLine(); + $cheapCheck = ( + $method->getNumberOfParameters() == 0 + && substr($method->getName(), 0, 3) == 'get' + && in_array($identifier, $class->getIdentifier(), true) + && $class->hasField($identifier) + && (($endLine - $startLine) <= 4) + ); + + if ($cheapCheck) { + $code = file($method->getDeclaringClass()->getFileName()); + $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1))); + + $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); + + if (preg_match($pattern, $code)) { + return true; + } + } + + return false; + } + + /** + * Generates the list of public properties to be lazy loaded, with their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return mixed[] + */ + private function getLazyLoadedPublicProperties(ClassMetadata $class) + { + $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); + $properties = []; + + foreach ($class->getReflectionClass()->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $name = $property->getName(); + + if (($class->hasField($name) || $class->hasAssociation($name)) && ! $class->isIdentifier($name)) { + $properties[$name] = $defaultProperties[$name]; + } + } + + return $properties; + } + + /** + * @param ClassMetadata $class + * @param \ReflectionMethod $method + * @param \ReflectionParameter[] $parameters + * + * @return string + */ + private function buildParametersString(ClassMetadata $class, \ReflectionMethod $method, array $parameters) + { + $parameterDefinitions = []; + + /* @var $param \ReflectionParameter */ + foreach ($parameters as $param) { + $parameterDefinition = ''; + + if ($parameterType = $this->getParameterType($class, $method, $param)) { + $parameterDefinition .= $parameterType . ' '; + } + + if ($param->isPassedByReference()) { + $parameterDefinition .= '&'; + } + + if (method_exists($param, 'isVariadic') && $param->isVariadic()) { + $parameterDefinition .= '...'; + } + + $parameters[] = '$' . $param->getName(); + $parameterDefinition .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterDefinition .= ' = ' . var_export($param->getDefaultValue(), true); + } + + $parameterDefinitions[] = $parameterDefinition; + } + + return implode(', ', $parameterDefinitions); + } + + /** + * @param ClassMetadata $class + * @param \ReflectionMethod $method + * @param \ReflectionParameter $parameter + * + * @return string|null + */ + private function getParameterType(ClassMetadata $class, \ReflectionMethod $method, \ReflectionParameter $parameter) + { + + // We need to pick the type hint class too + if ($parameter->isArray()) { + return 'array'; + } + + if ($parameter->isCallable()) { + return 'callable'; + } + + if (method_exists($parameter, 'hasType') && $parameter->hasType() && $parameter->getType()->isBuiltin()) { + return (string) $parameter->getType(); + } + + try { + $parameterClass = $parameter->getClass(); + + if ($parameterClass) { + return '\\' . $parameterClass->getName(); + } + } catch (\ReflectionException $previous) { + throw UnexpectedValueException::invalidParameterTypeHint( + $class->getName(), + $method->getName(), + $parameter->getName(), + $previous + ); + } + + return null; + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForInvoke(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + return '$' . $parameter->getName(); + }, + $parameters + ); + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForParentCall(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + $name = ''; + + if (method_exists($parameter, 'isVariadic') && $parameter->isVariadic()) { + $name .= '...'; + } + + $name .= '$' . $parameter->getName(); + + return $name; + }, + $parameters + ); + } + + /** + * @Param \ReflectionMethod $method + * + * @return string + */ + private function getMethodReturnType(\ReflectionMethod $method) + { + if (! (method_exists($method, 'hasReturnType') && $method->hasReturnType())) { + return ''; + } + + $returnType = $method->getReturnType(); + + if ($returnType->isBuiltin()) { + return ': ' . $returnType; + } + + $nameLower = strtolower((string) $returnType); + + if ('self' === $nameLower) { + return ': \\' . $method->getDeclaringClass()->getName(); + } + + if ('parent' === $nameLower) { + return ': \\' . $method->getDeclaringClass()->getParentClass()->getName(); + } + + return ': \\' . (string) $returnType; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php new file mode 100644 index 00000000000..639fd69a616 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +interface ClassFinderInterface +{ + /** + * Finds a class. + * + * @param string $class The name of the class. + * + * @return string|null The name of the class or NULL if not found. + */ + public function findFile($class); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php new file mode 100644 index 00000000000..418bb0f7b99 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +class Psr0FindFile implements ClassFinderInterface +{ + /** + * The PSR-0 prefixes. + * + * @var array + */ + protected $prefixes; + + /** + * @param array $prefixes An array of prefixes. Each key is a PHP namespace and each value is + * a list of directories. + */ + public function __construct($prefixes) + { + $this->prefixes = $prefixes; + } + + /** + * {@inheritDoc} + */ + public function findFile($class) + { + $lastNsPos = strrpos($class, '\\'); + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $lastNsPos) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $lastNsPos)) . DIRECTORY_SEPARATOR; + $className = substr($class, $lastNsPos + 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 (is_file($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + + return null; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php new file mode 100644 index 00000000000..3d970eeedb4 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +interface ReflectionProviderInterface +{ + /** + * Gets the ReflectionClass equivalent for this class. + * + * @return \ReflectionClass + */ + public function getReflectionClass(); + + /** + * Gets the ReflectionMethod equivalent for this class. + * + * @param string $name + * + * @return \ReflectionMethod + */ + public function getReflectionMethod($name); + + /** + * Gets the ReflectionProperty equivalent for this class. + * + * @param string $name + * + * @return \ReflectionProperty + */ + public function getReflectionProperty($name); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php new file mode 100644 index 00000000000..4e8ef5303ab --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use Doctrine\Common\Proxy\Proxy; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Public Property - special overrides for public properties. + * + * @author Marco Pivetta + * @since 2.4 + */ +class RuntimePublicReflectionProperty extends ReflectionProperty +{ + /** + * {@inheritDoc} + * + * Checks is the value actually exist before fetching it. + * This is to avoid calling `__get` on the provided $object if it + * is a {@see \Doctrine\Common\Proxy\Proxy}. + */ + public function getValue($object = null) + { + $name = $this->getName(); + + if ($object instanceof Proxy && ! $object->__isInitialized()) { + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + $val = isset($object->$name) ? $object->$name : null; + $object->__setInitializer($originalInitializer); + + return $val; + } + + return isset($object->$name) ? parent::getValue($object) : null; + } + + /** + * {@inheritDoc} + * + * Avoids triggering lazy loading via `__set` if the provided object + * is a {@see \Doctrine\Common\Proxy\Proxy}. + * @link https://bugs.php.net/bug.php?id=63463 + */ + public function setValue($object, $value = null) + { + if ( ! ($object instanceof Proxy && ! $object->__isInitialized())) { + parent::setValue($object, $value); + + return; + } + + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + parent::setValue($object, $value); + $object->__setInitializer($originalInitializer); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php new file mode 100644 index 00000000000..2d0f5b00776 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php @@ -0,0 +1,433 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionClass; +use ReflectionException; + +class StaticReflectionClass extends ReflectionClass +{ + /** + * The static reflection parser object. + * + * @var StaticReflectionParser + */ + private $staticReflectionParser; + + /** + * @param StaticReflectionParser $staticReflectionParser + */ + public function __construct(StaticReflectionParser $staticReflectionParser) + { + $this->staticReflectionParser = $staticReflectionParser; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->staticReflectionParser->getClassName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->staticReflectionParser->getDocComment(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->staticReflectionParser->getNamespaceName(); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->staticReflectionParser->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public function getMethod($name) + { + return $this->staticReflectionParser->getReflectionMethod($name); + } + + /** + * {@inheritDoc} + */ + public function getProperty($name) + { + return $this->staticReflectionParser->getReflectionProperty($name); + } + + /** + * {@inheritDoc} + */ + public static function export($argument, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstants() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getDefaultProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaceNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaces() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getMethods($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParentClass() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getProperties($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticPropertyValue($name, $default = '') + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitAliases() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraits() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasMethod($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasProperty($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function implementsInterface($interface) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isCloneable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstance($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstantiable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInterface() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isIterateable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isSubclassOf($class) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isTrait() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstance($args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceArgs(array $args = []) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceWithoutConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setStaticPropertyValue($name, $value) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php new file mode 100644 index 00000000000..8d5380b322c --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php @@ -0,0 +1,362 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use ReflectionMethod; + +class StaticReflectionMethod extends ReflectionMethod +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the method. + * + * @var string + */ + protected $methodName; + + /** + * @param StaticReflectionParser $staticReflectionParser + * @param string $methodName + */ + public function __construct(StaticReflectionParser $staticReflectionParser, $methodName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->methodName = $methodName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->methodName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('method', $this->methodName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->getStaticReflectionParser()->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('method', $this->methodName); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosure($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getPrototype() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invoke($object, $parameter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invokeArgs($object, array $args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDestructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosureThis() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfRequiredParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticVariables() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isClosure() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDeprecated() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function returnsReference() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php new file mode 100644 index 00000000000..c48e9ba73a5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php @@ -0,0 +1,307 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use Doctrine\Common\Annotations\TokenParser; +use ReflectionException; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Karoly Negyesi + */ +class StaticReflectionParser implements ReflectionProviderInterface +{ + /** + * The fully qualified class name. + * + * @var string + */ + protected $className; + + /** + * The short class name. + * + * @var string + */ + protected $shortClassName; + + /** + * Whether the caller only wants class annotations. + * + * @var boolean. + */ + protected $classAnnotationOptimize; + + /** + * Whether the parser has run. + * + * @var boolean + */ + protected $parsed = false; + + /** + * The namespace of the class. + * + * @var string + */ + protected $namespace = ''; + + /** + * The use statements of the class. + * + * @var array + */ + protected $useStatements = []; + + /** + * The docComment of the class. + * + * @var string + */ + protected $docComment = [ + 'class' => '', + 'property' => [], + 'method' => [] + ]; + + /** + * The name of the class this class extends, if any. + * + * @var string + */ + protected $parentClassName = ''; + + /** + * The parent PSR-0 Parser. + * + * @var \Doctrine\Common\Reflection\StaticReflectionParser + */ + protected $parentStaticReflectionParser; + + /** + * Parses a class residing in a PSR-0 hierarchy. + * + * @param string $className The full, namespaced class name. + * @param ClassFinderInterface $finder A ClassFinder object which finds the class. + * @param boolean $classAnnotationOptimize Only retrieve the class docComment. + * Presumes there is only one statement per line. + */ + public function __construct($className, $finder, $classAnnotationOptimize = false) + { + $this->className = ltrim($className, '\\'); + $lastNsPos = strrpos($this->className, '\\'); + + if ($lastNsPos !== false) { + $this->namespace = substr($this->className, 0, $lastNsPos); + $this->shortClassName = substr($this->className, $lastNsPos + 1); + } else { + $this->shortClassName = $this->className; + } + + $this->finder = $finder; + $this->classAnnotationOptimize = $classAnnotationOptimize; + } + + /** + * @return void + */ + protected function parse() + { + if ($this->parsed || !$fileName = $this->finder->findFile($this->className)) { + return; + } + $this->parsed = true; + $contents = file_get_contents($fileName); + if ($this->classAnnotationOptimize) { + if (preg_match("/\A.*^\s*((abstract|final)\s+)?class\s+{$this->shortClassName}\s+/sm", $contents, $matches)) { + $contents = $matches[0]; + } + } + $tokenParser = new TokenParser($contents); + $docComment = ''; + while ($token = $tokenParser->next(false)) { + if (is_array($token)) { + switch ($token[0]) { + case T_USE: + $this->useStatements = array_merge($this->useStatements, $tokenParser->parseUseStatement()); + break; + case T_DOC_COMMENT: + $docComment = $token[1]; + break; + case T_CLASS: + $this->docComment['class'] = $docComment; + $docComment = ''; + break; + case T_VAR: + case T_PRIVATE: + case T_PROTECTED: + case T_PUBLIC: + $token = $tokenParser->next(); + if ($token[0] === T_VARIABLE) { + $propertyName = substr($token[1], 1); + $this->docComment['property'][$propertyName] = $docComment; + continue 2; + } + if ($token[0] !== T_FUNCTION) { + // For example, it can be T_FINAL. + continue 2; + } + // No break. + case T_FUNCTION: + // The next string after function is the name, but + // there can be & before the function name so find the + // string. + while (($token = $tokenParser->next()) && $token[0] !== T_STRING); + $methodName = $token[1]; + $this->docComment['method'][$methodName] = $docComment; + $docComment = ''; + break; + case T_EXTENDS: + $this->parentClassName = $tokenParser->parseClass(); + $nsPos = strpos($this->parentClassName, '\\'); + $fullySpecified = false; + if ($nsPos === 0) { + $fullySpecified = true; + } else { + if ($nsPos) { + $prefix = strtolower(substr($this->parentClassName, 0, $nsPos)); + $postfix = substr($this->parentClassName, $nsPos); + } else { + $prefix = strtolower($this->parentClassName); + $postfix = ''; + } + foreach ($this->useStatements as $alias => $use) { + if ($alias == $prefix) { + $this->parentClassName = '\\' . $use . $postfix; + $fullySpecified = true; + } + } + } + if (!$fullySpecified) { + $this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName; + } + break; + } + } + } + } + + /** + * @return StaticReflectionParser + */ + protected function getParentStaticReflectionParser() + { + if (empty($this->parentStaticReflectionParser)) { + $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder); + } + + return $this->parentStaticReflectionParser; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->className; + } + + /** + * @return string + */ + public function getNamespaceName() + { + return $this->namespace; + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + return new StaticReflectionClass($this); + } + + /** + * {@inheritDoc} + */ + public function getReflectionMethod($methodName) + { + return new StaticReflectionMethod($this, $methodName); + } + + /** + * {@inheritDoc} + */ + public function getReflectionProperty($propertyName) + { + return new StaticReflectionProperty($this, $propertyName); + } + + /** + * Gets the use statements from this file. + * + * @return array + */ + public function getUseStatements() + { + $this->parse(); + + return $this->useStatements; + } + + /** + * Gets the doc comment. + * + * @param string $type The type: 'class', 'property' or 'method'. + * @param string $name The name of the property or method, not needed for 'class'. + * + * @return string The doc comment, empty string if none. + */ + public function getDocComment($type = 'class', $name = '') + { + $this->parse(); + + return $name ? $this->docComment[$type][$name] : $this->docComment[$type]; + } + + /** + * Gets the PSR-0 parser for the declaring class. + * + * @param string $type The type: 'property' or 'method'. + * @param string $name The name of the property or method. + * + * @return StaticReflectionParser A static reflection parser for the declaring class. + * + * @throws ReflectionException + */ + public function getStaticReflectionParserForDeclaringClass($type, $name) + { + $this->parse(); + if (isset($this->docComment[$type][$name])) { + return $this; + } + if (!empty($this->parentClassName)) { + return $this->getParentStaticReflectionParser()->getStaticReflectionParserForDeclaringClass($type, $name); + } + throw new ReflectionException('Invalid ' . $type . ' "' . $name . '"'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php new file mode 100644 index 00000000000..7f6522bbac5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use ReflectionProperty; + +class StaticReflectionProperty extends ReflectionProperty +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the property. + * + * @var string|null + */ + protected $propertyName; + + /** + * @param StaticReflectionParser $staticReflectionParser + * @param string|null $propertyName + */ + public function __construct(StaticReflectionParser $staticReflectionParser, $propertyName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->propertyName = $propertyName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->propertyName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', $this->propertyName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('property', $this->propertyName); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getValue($object = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDefault() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setValue($object, $value = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} 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 00000000000..49dc7bb1722 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php @@ -0,0 +1,109 @@ +. + */ + +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 +{ + /** + * Gets the real class name of a class name that could be a proxy. + * + * @param string $class + * + * @return string + */ + public static function getRealClass($class) + { + if (false === $pos = strrpos($class, '\\'.Proxy::MARKER.'\\')) { + return $class; + } + + return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + } + + /** + * Gets the real class name of an object (even if its a proxy). + * + * @param object $object + * + * @return string + */ + public static function getClass($object) + { + return self::getRealClass(get_class($object)); + } + + /** + * Gets the real parent class name of a class or object. + * + * @param string $className + * + * @return string + */ + public static function getParentClass($className) + { + return get_parent_class( self::getRealClass( $className ) ); + } + + /** + * Creates a new reflection class. + * + * @param string $class + * + * @return \ReflectionClass + */ + public static function newReflectionClass($class) + { + return new \ReflectionClass( self::getRealClass( $class ) ); + } + + /** + * Creates a new reflection object. + * + * @param object $object + * + * @return \ReflectionObject + */ + public static function newReflectionObject($object) + { + return self::newReflectionClass( self::getClass( $object ) ); + } + + /** + * Given a class name and a proxy namespace returns 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 00000000000..7f675328f80 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php @@ -0,0 +1,158 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\Proxy; + +/** + * Static class containing most used debug methods. + * + * @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 instantiation). + */ + private function __construct() + { + } + + /** + * Prints a dump of the public, protected and private properties of $var. + * + * @link http://xdebug.org/ + * + * @param mixed $var The variable to dump. + * @param integer $maxDepth The maximum nesting level for object properties. + * @param boolean $stripTags Whether output should strip HTML tags. + * @param boolean $echo Send the dumped value to the output buffer + * + * @return string + */ + public static function dump($var, $maxDepth = 2, $stripTags = true, $echo = true) + { + $html = ini_get('html_errors'); + + if ($html !== true) { + ini_set('html_errors', true); + } + + 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(); + + $dumpText = ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); + + ini_set('html_errors', $html); + + if ($echo) { + echo $dumpText; + } + + return $dumpText; + } + + /** + * @param mixed $var + * @param int $maxDepth + * + * @return mixed + */ + public static function export($var, $maxDepth) + { + $return = null; + $isObj = is_object($var); + + if ($var instanceof Collection) { + $var = $var->toArray(); + } + + if ($maxDepth) { + if (is_array($var)) { + $return = []; + + 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 Proxy) { + $return->__IS_PROXY__ = true; + $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); + } + + if ($var instanceof \ArrayObject || $var instanceof \ArrayIterator) { + $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); + } + + 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; + } + + /** + * Returns a string representation of an object. + * + * @param object $obj + * + * @return string + */ + public static function toString($obj) + { + return method_exists($obj, '__toString') ? (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 00000000000..082dc789448 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Inflector\Inflector as BaseInflector; + +/** + * Doctrine inflector has static methods for inflecting text. + * + * Kept for backwards compatibility reasons, was moved to its own component. + */ +class Inflector extends BaseInflector +{ +} 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 00000000000..7baa65438a4 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Version.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Class to store and retrieve the version of Doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version. + */ + const VERSION = '2.6.1'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * + * @return int -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/LICENSE b/vendor/doctrine/dbal/LICENSE new file mode 100644 index 00000000000..4a91f0bf280 --- /dev/null +++ b/vendor/doctrine/dbal/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 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/dbal/README.md b/vendor/doctrine/dbal/README.md new file mode 100644 index 00000000000..b9956306add --- /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) [![Dependency Status](https://www.versioneye.com/php/doctrine:dbal/dev-master/badge.png)](https://www.versioneye.com/php/doctrine:dbal/dev-master) +* 2.4: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.4)](http://travis-ci.org/doctrine/dbal) [![Dependency Status](https://www.versioneye.com/php/doctrine:dbal/2.4.2/badge.png)](https://www.versioneye.com/php/doctrine:dbal/2.4.2) +* 2.3: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.3)](http://travis-ci.org/doctrine/dbal) [![Dependency Status](https://www.versioneye.com/php/doctrine:dbal/2.3.4/badge.png)](https://www.versioneye.com/php/doctrine:dbal/2.3.4) + +## More resources: + +* [Website](http://www.doctrine-project.org/projects/dbal.html) +* [Documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DBAL) +* [Downloads](http://github.com/doctrine/dbal/downloads) diff --git a/vendor/doctrine/dbal/SECURITY.md b/vendor/doctrine/dbal/SECURITY.md new file mode 100644 index 00000000000..e18f0dd78ab --- /dev/null +++ b/vendor/doctrine/dbal/SECURITY.md @@ -0,0 +1,14 @@ +Security +======== + +The Doctrine library is operating very close to your database and as such needs +to handle and make assumptions about SQL injection vulnerabilities. + +It is vital that you understand how Doctrine approaches security, because +we cannot protect you from SQL injection. + +Please read the documentation chapter on Security in Doctrine DBAL to +understand the assumptions we make. + +- [Latest security.rst page on Github](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst) +- [Security Page in rendered documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html) diff --git a/vendor/doctrine/dbal/UPGRADE.md b/vendor/doctrine/dbal/UPGRADE.md new file mode 100644 index 00000000000..1d628eef70e --- /dev/null +++ b/vendor/doctrine/dbal/UPGRADE.md @@ -0,0 +1,244 @@ +# Upgrade to 2.5.1 + +## MINOR BC BREAK: Doctrine\DBAL\Schema\Table + +When adding indexes to ``Doctrine\DBAL\Schema\Table`` via ``addIndex()`` or ``addUniqueIndex()``, +duplicate indexes are not silently ignored/dropped anymore (based on semantics, not naming!). +Duplicate indexes are considered indexes that pass ``isFullfilledBy()`` or ``overrules()`` +in ``Doctrine\DBAL\Schema\Index``. +This is required to make the index renaming feature introduced in 2.5.0 work properly and avoid +issues in the ORM schema tool / DBAL schema manager which pretends users from updating +their schemas and migrate to DBAL 2.5.*. +Additionally it offers more flexibility in declaring indexes for the user and potentially fixes +related issues in the ORM. +With this change, the responsibility to decide which index is a "duplicate" is completely deferred +to the user. +Please also note that adding foreign key constraints to a table via ``addForeignKeyConstraint()``, +``addUnnamedForeignKeyConstraint()`` or ``addNamedForeignKeyConstraint()`` now first checks if an +appropriate index is already present and avoids adding an additional auto-generated one eventually. + +# Upgrade to 2.5 + +## BC BREAK: time type resets date fields to UNIX epoch + +When mapping `time` type field to PHP's `DateTime` instance all unused date fields are +reset to UNIX epoch (i.e. 1970-01-01). This might break any logic which relies on comparing +`DateTime` instances with date fields set to the current date. + +Use `!` format prefix (see http://php.net/manual/en/datetime.createfromformat.php) for parsing +time strings to prevent having different date fields when comparing user input and `DateTime` +instances as mapped by Doctrine. + +## BC BREAK: Doctrine\DBAL\Schema\Table + +The methods ``addIndex()`` and ``addUniqueIndex()`` in ``Doctrine\DBAL\Schema\Table`` +have an additional, optional parameter. If you override these methods, you should +add this new parameter to the declaration of your overridden methods. + +## BC BREAK: Doctrine\DBAL\Connection + +The visibility of the property ``$_platform`` in ``Doctrine\DBAL\Connection`` +was changed from protected to private. If you have subclassed ``Doctrine\DBAL\Connection`` +in your application and accessed ``$_platform`` directly, you have to change the code +portions to use ``getDatabasePlatform()`` instead to retrieve the underlying database +platform. +The reason for this change is the new automatic platform version detection feature, +which lazily evaluates the appropriate platform class to use for the underlying database +server version at runtime. +Please also note, that calling ``getDatabasePlatform()`` now needs to establish a connection +in order to evaluate the appropriate platform class if ``Doctrine\DBAL\Connection`` is not +already connected. Under the following circumstances, it is not possible anymore to retrieve +the platform instance from the connection object without having to do a real connect: + +1. ``Doctrine\DBAL\Connection`` was instantiated without the ``platform`` connection parameter. +2. ``Doctrine\DBAL\Connection`` was instantiated without the ``serverVersion`` connection parameter. +3. The underlying driver is "version aware" and can provide different platform instances + for different versions. +4. The underlying driver connection is "version aware" and can provide the database server + version without having to query for it. + +If one of the above conditions is NOT met, there is no need for ``Doctrine\DBAL\Connection`` +to do a connect when calling ``getDatabasePlatform()``. + +## datetime Type uses date_create() as fallback + +Before 2.5 the DateTime type always required a specific format, defined in +`$platform->getDateTimeFormatString()`, which could cause quite some troubles +on platforms that had various microtime precision formats. Starting with 2.5 +whenever the parsing of a date fails with the predefined platform format, +the `date_create()` function will be used to parse the date. + +This could cause some troubles when your date format is weird and not parsed +correctly by `date_create`, however since databases are rather strict on dates +there should be no problem. + +## Support for pdo_ibm driver removed + +The ``pdo_ibm`` driver is buggy and does not work well with Doctrine. Therefore it will no +longer be supported and has been removed from the ``Doctrine\DBAL\DriverManager`` drivers +map. It is highly encouraged to to use `ibm_db2` driver instead if you want to connect +to an IBM DB2 database as it is much more stable and secure. + +If for some reason you have to utilize the ``pdo_ibm`` driver you can still use the `driverClass` +connection parameter to explicitly specify the ``Doctrine\DBAL\Driver\PDOIbm\Driver`` class. +However be aware that you are doing this at your own risk and it will not be guaranteed that +Doctrine will work as expected. + +# Upgrade to 2.4 + +## Doctrine\DBAL\Schema\Constraint + +If you have custom classes that implement the constraint interface, you have to implement +an additional method ``getQuotedColumns`` now. This method is used to build proper constraint +SQL for columns that need to be quoted, like keywords reserved by the specific platform used. +The method has to return the same values as ``getColumns`` only that those column names that +need quotation have to be returned quoted for the given platform. + +# Upgrade to 2.3 + +## Oracle Session Init now sets Numeric Character + +Before 2.3 the Oracle Session Init did not care about the numeric character of the Session. +This could lead to problems on non english locale systems that required a comma as a floating +point seperator in Oracle. Since 2.3, using the Oracle Session Init on connection start the +client session will be altered to set the numeric character to ".,": + + ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,' + +See [DBAL-345](http://www.doctrine-project.org/jira/browse/DBAL-345) for more details. + +## Doctrine\DBAL\Connection and Doctrine\DBAL\Statement + +The query related methods including but not limited to executeQuery, exec, query, and executeUpdate +now wrap the driver exceptions such as PDOException with DBALException to add more debugging +information such as the executed SQL statement, and any bound parameters. + +If you want to retrieve the driver specific exception, you can retrieve it by calling the +``getPrevious()`` method on DBALException. + +Before: + + catch(\PDOException $ex) { + // ... + } + +After: + + catch(\Doctrine\DBAL\DBALException $ex) { + $pdoException = $ex->getPrevious(); + // ... + } + +## Doctrine\DBAL\Connection#setCharsetSQL() removed + +This method only worked on MySQL and it is considered unsafe on MySQL to use SET NAMES UTF-8 instead +of setting the charset directly on connection already. Replace this behavior with the +connection charset option: + +Before: + + $conn = DriverManager::getConnection(array(..)); + $conn->setCharset('UTF8'); + +After: + + $conn = DriverManager::getConnection(array('charset' => 'UTF8', ..)); + +## Doctrine\DBAL\Schema\Table#renameColumn() removed + +Doctrine\DBAL\Schema\Table#renameColumn() was removed, because it drops and recreates +the column instead. There is no fix available, because a schema diff +cannot reliably detect if a column was renamed or one column was created +and another one dropped. + +You should use explicit SQL ALTER TABLE statements to change columns names. + +## Schema Filter paths + +The Filter Schema assets expression is not wrapped in () anymore for the regexp automatically. + +Before: + + $config->setFilterSchemaAssetsExpression('foo'); + +After: + + $config->setFilterSchemaAssetsExpression('(foo)'); + +## Creating MySQL Tables now defaults to UTF-8 + +If you are creating a new MySQL Table through the Doctrine API, charset/collate are +now set to 'utf8'/'utf8_unicode_ci' by default. Previously the MySQL server defaults were used. + +# Upgrade to 2.2 + +## Doctrine\DBAL\Connection#insert and Doctrine\DBAL\Connection#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 separates 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/bin/doctrine-dbal b/vendor/doctrine/dbal/bin/doctrine-dbal new file mode 100644 index 00000000000..0531527ddca --- /dev/null +++ b/vendor/doctrine/dbal/bin/doctrine-dbal @@ -0,0 +1,4 @@ +#!/usr/bin/env php +. + */ + +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\DBAL\Tools\Console\ConsoleRunner; + +$files = array(__DIR__ . '/../vendor/autoload.php', __DIR__ . '/../../../autoload.php'); +$loader = null; +$cwd = getcwd(); +$directories = array($cwd, $cwd . DIRECTORY_SEPARATOR . 'config'); +$configFile = null; + +foreach ($files as $file) { + if (file_exists($file)) { + $loader = require $file; + + break; + } +} + +if ( ! $loader) { + throw new RuntimeException('vendor/autoload.php could not be found. Did you run `php composer.phar install`?'); +} + +foreach ($directories as $directory) { + $configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php'; + + if (file_exists($configFile)) { + break; + } +} + +if ( ! file_exists($configFile)) { + ConsoleRunner::printCliConfigTemplate(); + + exit(1); +} + +if ( ! is_readable($configFile)) { + echo 'Configuration file [' . $configFile . '] does not have read permission.' . PHP_EOL; + + exit(1); +} + +$commands = array(); +$helperSet = require $configFile; + +if ( ! $helperSet instanceof HelperSet) { + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof HelperSet) { + $helperSet = $helperSetCandidate; + + break; + } + } +} + +ConsoleRunner::run($helperSet, $commands); diff --git a/vendor/doctrine/dbal/composer.json b/vendor/doctrine/dbal/composer.json new file mode 100644 index 00000000000..049019ef146 --- /dev/null +++ b/vendor/doctrine/dbal/composer.json @@ -0,0 +1,37 @@ +{ + "name": "doctrine/dbal", + "type": "library", + "description": "Database Abstraction Layer", + "keywords": ["dbal", "database", "persistence", "queryobject"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "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.4,<2.7-dev" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": ["bin/doctrine-dbal"], + "autoload": { + "psr-0": { "Doctrine\\DBAL\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "archive": { + "exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar"] + } +} 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 00000000000..0752dda31bd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\ResultStatement; +use PDO; + +class ArrayStatement implements \IteratorAggregate, ResultStatement +{ + /** + * @var array + */ + private $data; + + /** + * @var integer + */ + private $columnCount = 0; + + /** + * @var integer + */ + private $num = 0; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param array $data + */ + public function __construct(array $data) + { + $this->data = $data; + if (count($data)) { + $this->columnCount = count($data[0]); + } + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + unset ($this->data); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->columnCount; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 !== null || $arg3 !== null) { + throw new \InvalidArgumentException("Caching layer does not support 2nd/3rd argument to setFetchMode()"); + } + + $this->defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + if (isset($this->data[$this->num])) { + $row = $this->data[$this->num++]; + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + if ($fetchMode === PDO::FETCH_ASSOC) { + return $row; + } elseif ($fetchMode === PDO::FETCH_NUM) { + return array_values($row); + } elseif ($fetchMode === PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } elseif ($fetchMode === PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for fetching result."); + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..6b3b5394bbb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +/** + * @author Benjamin Eberlei + * @since 2.2 + */ +class CacheException extends \Doctrine\DBAL\DBALException +{ + /** + * @return \Doctrine\DBAL\Cache\CacheException + */ + static public function noCacheKey() + { + return new self("No cache key was set."); + } + + /** + * @return \Doctrine\DBAL\Cache\CacheException + */ + static public function noResultDriverConfigured() + { + return new self("Trying to cache a query but no result driver is configured."); + } +} 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 00000000000..330eec91f23 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php @@ -0,0 +1,141 @@ +. + */ + +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 \Doctrine\Common\Cache\Cache|null + */ + private $resultCacheDriver; + + /** + * @var integer + */ + private $lifetime = 0; + + /** + * @var string|null + */ + private $cacheKey; + + /** + * @param integer $lifetime + * @param string|null $cacheKey + * @param \Doctrine\Common\Cache\Cache|null $resultCache + */ + public function __construct($lifetime = 0, $cacheKey = null, Cache $resultCache = null) + { + $this->lifetime = $lifetime; + $this->cacheKey = $cacheKey; + $this->resultCacheDriver = $resultCache; + } + + /** + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getResultCacheDriver() + { + return $this->resultCacheDriver; + } + + /** + * @return integer + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\Cache\CacheException + */ + public function getCacheKey() + { + if ($this->cacheKey === null) { + throw CacheException::noCacheKey(); + } + + return $this->cacheKey; + } + + /** + * Generates 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 \Doctrine\Common\Cache\Cache $cache + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setResultCacheDriver(Cache $cache) + { + return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + } + + /** + * @param string|null $cacheKey + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setCacheKey($cacheKey) + { + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + } + + /** + * @param integer $lifetime + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setLifetime($lifetime) + { + return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver); + } +} 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 00000000000..0062255e5d2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -0,0 +1,221 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\ResultStatement; +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 integer + */ + private $lifetime; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $statement; + + /** + * Did we reach the end of the statement? + * + * @var boolean + */ + private $emptied = false; + + /** + * @var array + */ + private $data; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\Common\Cache\Cache $resultCache + * @param string $cacheKey + * @param string $realKey + * @param integer $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; + } + + /** + * {@inheritdoc} + */ + 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); + } + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->statement->columnCount(); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + if ($this->data === null) { + $this->data = array(); + } + + $row = $this->statement->fetch(PDO::FETCH_ASSOC); + if ($row) { + $this->data[] = $row; + + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($fetchMode == PDO::FETCH_ASSOC) { + return $row; + } elseif ($fetchMode == PDO::FETCH_NUM) { + return array_values($row); + } elseif ($fetchMode == PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } elseif ($fetchMode == PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for caching result."); + } + } + $this->emptied = true; + + return false; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + 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]; + } + + /** + * 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 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 00000000000..ec7c130619c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php @@ -0,0 +1,152 @@ +. + */ + +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 \Doctrine\DBAL\Logging\SQLLogger|null $logger + * + * @return void + */ + public function setSQLLogger(SQLLogger $logger = null) + { + $this->_attributes['sqlLogger'] = $logger; + } + + /** + * Gets the SQL logger that is used. + * + * @return \Doctrine\DBAL\Logging\SQLLogger|null + */ + 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|null + */ + 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 + * + * @return void + */ + public function setResultCacheImpl(Cache $cacheImpl) + { + $this->_attributes['resultCacheImpl'] = $cacheImpl; + } + + /** + * Sets the 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 + * + * @return void + */ + public function setFilterSchemaAssetsExpression($filterExpression) + { + $this->_attributes['filterSchemaAssetsExpression'] = $filterExpression; + } + + /** + * Returns filter schema assets expression. + * + * @return string|null + */ + public function getFilterSchemaAssetsExpression() + { + if (isset($this->_attributes['filterSchemaAssetsExpression'])) { + return $this->_attributes['filterSchemaAssetsExpression']; + } + + return null; + } + + /** + * Sets the default auto-commit mode for connections. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * @param boolean $autoCommit True to enable auto-commit mode; false to disable it. + * + * @see getAutoCommit + */ + public function setAutoCommit($autoCommit) + { + $this->_attributes['autoCommit'] = (boolean) $autoCommit; + } + + /** + * Returns the default auto-commit mode for connections. + * + * @return boolean True if auto-commit mode is enabled by default for connections, false otherwise. + * + * @see setAutoCommit + */ + public function getAutoCommit() + { + if (isset($this->_attributes['autoCommit'])) { + return $this->_attributes['autoCommit']; + } + + return true; + } +} 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 00000000000..8612540d6e9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php @@ -0,0 +1,1607 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Exception\InvalidArgumentException; +use PDO; +use Closure; +use Exception; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Cache\ResultCacheStatement; +use Doctrine\DBAL\Cache\QueryCacheProfile; +use Doctrine\DBAL\Cache\ArrayStatement; +use Doctrine\DBAL\Cache\CacheException; +use Doctrine\DBAL\Driver\PingableConnection; + +/** + * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like + * events, transaction isolation levels, configuration, emulated transaction nesting, + * lazy connecting and more. + * + * @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 integer + */ + const PARAM_INT_ARRAY = 101; + + /** + * Represents an array of strings to be expanded by Doctrine SQL parsing. + * + * @var integer + */ + const PARAM_STR_ARRAY = 102; + + /** + * Offset by which PARAM_* constants are detected as arrays of the param type. + * + * @var integer + */ + 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\Expression\ExpressionBuilder + */ + protected $_expr; + + /** + * Whether or not a connection has been established. + * + * @var boolean + */ + private $_isConnected = false; + + /** + * The current auto-commit mode of this connection. + * + * @var boolean + */ + private $autoCommit = true; + + /** + * The transaction nesting level. + * + * @var integer + */ + private $_transactionNestingLevel = 0; + + /** + * The currently active transaction isolation level. + * + * @var integer + */ + private $_transactionIsolationLevel; + + /** + * If nested transactions should use savepoints. + * + * @var boolean + */ + private $_nestTransactionsWithSavepoints = false; + + /** + * 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 + */ + private $platform; + + /** + * The schema manager. + * + * @var \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + 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; + + /** + * @var integer + */ + protected $defaultFetchMode = PDO::FETCH_ASSOC; + + /** + * Initializes a new instance of the Connection class. + * + * @param array $params The connection parameters. + * @param \Doctrine\DBAL\Driver $driver The driver to use. + * @param \Doctrine\DBAL\Configuration|null $config The configuration, optional. + * @param \Doctrine\Common\EventManager|null $eventManager The event manager, optional. + * + * @throws \Doctrine\DBAL\DBALException + */ + 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; + unset($this->_params['pdo']); + } + + // 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); + + $this->autoCommit = $config->getAutoCommit(); + } + + /** + * Gets the parameters used during instantiation. + * + * @return array + */ + public function getParams() + { + return $this->_params; + } + + /** + * Gets the name of the database this Connection is connected to. + * + * @return string + */ + public function getDatabase() + { + return $this->_driver->getDatabase($this); + } + + /** + * Gets the hostname of the currently connected database. + * + * @return string|null + */ + 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|null + */ + public function getUsername() + { + return isset($this->_params['user']) ? $this->_params['user'] : null; + } + + /** + * Gets the password used by this connection. + * + * @return string|null + */ + 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() + { + if (null == $this->platform) { + $this->detectDatabasePlatform(); + } + + return $this->platform; + } + + /** + * Gets the ExpressionBuilder for the connection. + * + * @return \Doctrine\DBAL\Query\Expression\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 (null === $this->platform) { + $this->detectDatabasePlatform(); + } + + if (false === $this->autoCommit) { + $this->beginTransaction(); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Detects and sets the database platform. + * + * Evaluates custom platform class and version in order to set the correct platform. + * + * @throws DBALException if an invalid platform was specified for this connection. + */ + private function detectDatabasePlatform() + { + if ( ! isset($this->_params['platform'])) { + $version = $this->getDatabasePlatformVersion(); + + if (null !== $version) { + $this->platform = $this->_driver->createDatabasePlatformForVersion($version); + } else { + $this->platform = $this->_driver->getDatabasePlatform(); + } + } elseif ($this->_params['platform'] instanceof Platforms\AbstractPlatform) { + $this->platform = $this->_params['platform']; + } else { + throw DBALException::invalidPlatformSpecified(); + } + + $this->platform->setEventManager($this->_eventManager); + } + + /** + * Returns the version of the related platform if applicable. + * + * Returns null if either the driver is not capable to create version + * specific platform instances, no explicit server version was specified + * or the underlying driver connection cannot determine the platform + * version without having to query it (performance reasons). + * + * @return string|null + */ + private function getDatabasePlatformVersion() + { + // Driver does not support version specific platforms. + if ( ! $this->_driver instanceof VersionAwarePlatformDriver) { + return null; + } + + // Explicit platform version requested (supersedes auto-detection). + if (isset($this->_params['serverVersion'])) { + return $this->_params['serverVersion']; + } + + // If not connected, we need to connect now to determine the platform version. + if (null === $this->_conn) { + $this->connect(); + } + + // Automatic platform version detection. + if ($this->_conn instanceof ServerInfoAwareConnection && + ! $this->_conn->requiresQueryForServerVersion() + ) { + return $this->_conn->getServerVersion(); + } + + // Unable to detect platform version. + return null; + } + + /** + * Returns the current auto-commit mode for this connection. + * + * @return boolean True if auto-commit mode is currently enabled for this connection, false otherwise. + * + * @see setAutoCommit + */ + public function isAutoCommit() + { + return true === $this->autoCommit; + } + + /** + * Sets auto-commit mode for this connection. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is + * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op. + * + * @param boolean $autoCommit True to enable auto-commit mode; false to disable it. + * + * @see isAutoCommit + */ + public function setAutoCommit($autoCommit) + { + $autoCommit = (boolean) $autoCommit; + + // Mode not changed, no-op. + if ($autoCommit === $this->autoCommit) { + return; + } + + $this->autoCommit = $autoCommit; + + // Commit all currently active transactions if any when switching auto-commit mode. + if (true === $this->_isConnected && 0 !== $this->_transactionNestingLevel) { + $this->commitAll(); + } + } + + /** + * Sets the fetch mode. + * + * @param integer $fetchMode + * + * @return void + */ + public function setFetchMode($fetchMode) + { + $this->defaultFetchMode = $fetchMode; + } + + /** + * 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. + * @param array $types The query parameter types. + * + * @return array + */ + public function fetchAssoc($statement, array $params = array(), array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->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 The SQL query to be executed. + * @param array $params The prepared statement params. + * @param array $types The query parameter types. + * + * @return array + */ + public function fetchArray($statement, array $params = array(), array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->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 The SQL query to be executed. + * @param array $params The prepared statement params. + * @param integer $column The 0-indexed column number to retrieve. + * @param array $types The query parameter types. + * + * @return mixed + */ + public function fetchColumn($statement, array $params = array(), $column = 0, array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->fetchColumn($column); + } + + /** + * 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. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table on which to delete. + * @param array $identifier The deletion criteria. An associative array containing column-value pairs. + * @param array $types The types of identifiers. + * + * @return integer The number of affected rows. + * + * @throws InvalidArgumentException + */ + public function delete($tableExpression, array $identifier, array $types = array()) + { + if (empty($identifier)) { + throw InvalidArgumentException::fromEmptyCriteria(); + } + + $this->connect(); + + $criteria = array(); + + foreach (array_keys($identifier) as $columnName) { + $criteria[] = $columnName . ' = ?'; + } + + return $this->executeUpdate( + 'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $criteria), + array_values($identifier), + is_string(key($types)) ? $this->extractTypeValues($identifier, $types) : $types + ); + } + + /** + * Closes the connection. + * + * @return void + */ + public function close() + { + $this->_conn = null; + + $this->_isConnected = false; + } + + /** + * Sets the transaction isolation level. + * + * @param integer $level The level to set. + * + * @return integer + */ + public function setTransactionIsolation($level) + { + $this->_transactionIsolationLevel = $level; + + return $this->executeUpdate($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); + } + + /** + * Gets the currently active transaction isolation level. + * + * @return integer The current transaction isolation level. + */ + public function getTransactionIsolation() + { + if (null === $this->_transactionIsolationLevel) { + $this->_transactionIsolationLevel = $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); + } + + return $this->_transactionIsolationLevel; + } + + /** + * Executes an SQL UPDATE statement on a table. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table to update quoted or unquoted. + * @param array $data An associative array containing column-value pairs. + * @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($tableExpression, array $data, array $identifier, array $types = array()) + { + $this->connect(); + $set = array(); + + foreach ($data as $columnName => $value) { + $set[] = $columnName . ' = ?'; + } + + if (is_string(key($types))) { + $types = $this->extractTypeValues(array_merge($data, $identifier), $types); + } + + $params = array_merge(array_values($data), array_values($identifier)); + + $sql = 'UPDATE ' . $tableExpression . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) + . ' = ?'; + + return $this->executeUpdate($sql, $params, $types); + } + + /** + * Inserts a table row with specified data. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table to insert data into, quoted or unquoted. + * @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($tableExpression, array $data, array $types = array()) + { + $this->connect(); + + if (empty($data)) { + return $this->executeUpdate('INSERT INTO ' . $tableExpression . ' ()' . ' VALUES ()'); + } + + return $this->executeUpdate( + 'INSERT INTO ' . $tableExpression . ' (' . implode(', ', array_keys($data)) . ')' . + ' VALUES (' . implode(', ', array_fill(0, count($data), '?')) . ')', + array_values($data), + is_string(key($types)) ? $this->extractTypeValues($data, $types) : $types + ); + } + + /** + * Extract ordered type list from two associate key lists of data and types. + * + * @param array $data + * @param array $types + * + * @return array + */ + private function extractTypeValues(array $data, array $types) + { + $typeValues = array(); + + foreach ($data as $k => $_) { + $typeValues[] = isset($types[$k]) + ? $types[$k] + : \PDO::PARAM_STR; + } + + return $typeValues; + } + + /** + * Quotes 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->getDatabasePlatform()->quoteIdentifier($str); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input The parameter to be quoted. + * @param string|null $type The 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. + * @param array $types The query parameter types. + * + * @return array + */ + public function fetchAll($sql, array $params = array(), $types = array()) + { + return $this->executeQuery($sql, $params, $types)->fetchAll(); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * + * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function prepare($statement) + { + $this->connect(); + + try { + $stmt = new Statement($statement, $this); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, SQL query. + * + * If the query is parametrized, 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 \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional. + * + * @return \Doctrine\DBAL\Driver\Statement The executed statement. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + if ($qcp !== null) { + return $this->executeCacheQuery($query, $params, $types, $qcp); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + 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); + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types)); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $stmt; + } + + /** + * Executes a caching query. + * + * @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 \Doctrine\DBAL\Cache\QueryCacheProfile $qcp The query cache profile. + * + * @return \Doctrine\DBAL\Driver\ResultStatement + * + * @throws \Doctrine\DBAL\Cache\CacheException + */ + 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])) { + $stmt = new ArrayStatement($data[$realKey]); + } elseif (array_key_exists($realKey, $data)) { + $stmt = new ArrayStatement(array()); + } + } + + if (!isset($stmt)) { + $stmt = new ResultCacheStatement($this->executeQuery($query, $params, $types), $resultCache, $cacheKey, $realKey, $qcp->getLifetime()); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, 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 $function The transformation function that is applied on each row. + * The function receives a single parameter, an array, that + * represents a row of the result set. + * + * @return array The projected result of the query. + */ + public function project($query, array $params, Closure $function) + { + $result = array(); + $stmt = $this->executeQuery($query, $params); + + while ($row = $stmt->fetch()) { + $result[] = $function($row); + } + + $stmt->closeCursor(); + + return $result; + } + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement + * + * @throws \Doctrine\DBAL\DBALException + */ + public function query() + { + $this->connect(); + + $args = func_get_args(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + try { + switch (func_num_args()) { + case 1: + $statement = $this->_conn->query($args[0]); + break; + case 2: + $statement = $this->_conn->query($args[0], $args[1]); + break; + default: + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + break; + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $args[0]); + } + + $statement->setFetchMode($this->defaultFetchMode); + + 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. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + 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); + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types)); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Executes an SQL statement and return the number of affected rows. + * + * @param string $statement + * + * @return integer The number of affected rows. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function exec($statement) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($statement); + } + + try { + $result = $this->_conn->exec($statement); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * 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; + } + + /** + * Fetches the SQLSTATE associated with the last database operation. + * + * @return integer The last error code. + */ + public function errorCode() + { + $this->connect(); + + return $this->_conn->errorCode(); + } + + /** + * Fetches 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|null $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. + * + * @return void + * + * @throws \Exception + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $func($this); + $this->commit(); + } catch (Exception $e) { + $this->rollback(); + throw $e; + } + } + + /** + * Sets if nested transactions should use savepoints. + * + * @param boolean $nestTransactionsWithSavepoints + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + { + if ($this->_transactionNestingLevel > 0) { + throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); + } + + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints; + } + + /** + * Gets 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; + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"START TRANSACTION"'); + } + $this->_conn->beginTransaction(); + if ($logger) { + $logger->stopQuery(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"SAVEPOINT"'); + } + $this->createSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + } + + /** + * Commits the current transaction. + * + * @return void + * + * @throws \Doctrine\DBAL\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(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"COMMIT"'); + } + $this->_conn->commit(); + if ($logger) { + $logger->stopQuery(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"RELEASE SAVEPOINT"'); + } + $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + + --$this->_transactionNestingLevel; + + if (false === $this->autoCommit && 0 === $this->_transactionNestingLevel) { + $this->beginTransaction(); + } + } + + /** + * Commits all current nesting transactions. + */ + private function commitAll() + { + while (0 !== $this->_transactionNestingLevel) { + if (false === $this->autoCommit && 1 === $this->_transactionNestingLevel) { + // When in no auto-commit mode, the last nesting commit immediately starts a new transaction. + // Therefore we need to do the final commit here and then leave to avoid an infinite loop. + $this->commit(); + + return; + } + + $this->commit(); + } + } + + /** + * Cancels any database changes done during the current transaction. + * + * This method can be listened with onPreTransactionRollback and onTransactionRollback + * eventlistener methods. + * + * @throws \Doctrine\DBAL\ConnectionException If the rollback operation failed. + */ + public function rollBack() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"ROLLBACK"'); + } + $this->_transactionNestingLevel = 0; + $this->_conn->rollback(); + $this->_isRollbackOnly = false; + if ($logger) { + $logger->stopQuery(); + } + + if (false === $this->autoCommit) { + $this->beginTransaction(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); + } + $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); + --$this->_transactionNestingLevel; + if ($logger) { + $logger->stopQuery(); + } + } else { + $this->_isRollbackOnly = true; + --$this->_transactionNestingLevel; + } + } + + /** + * Creates a new savepoint. + * + * @param string $savepoint The name of the savepoint to create. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function createSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->platform->createSavePoint($savepoint)); + } + + /** + * Releases the given savepoint. + * + * @param string $savepoint The name of the savepoint to release. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function releaseSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + if ($this->platform->supportsReleaseSavepoints()) { + $this->_conn->exec($this->platform->releaseSavePoint($savepoint)); + } + } + + /** + * Rolls back to the given savepoint. + * + * @param string $savepoint The name of the savepoint to rollback to. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function rollbackSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->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. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If no transaction is active. + */ + public function setRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + $this->_isRollbackOnly = true; + } + + /** + * Checks whether the current transaction is marked for rollback only. + * + * @return boolean + * + * @throws \Doctrine\DBAL\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->getDatabasePlatform()); + } + + /** + * 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->getDatabasePlatform()); + } + + /** + * 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 \Doctrine\DBAL\Driver\Statement $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). + * + * @return void + * + * @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 $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->getDatabasePlatform()); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + + return array($value, $bindingType); + } + + /** + * Resolves the parameters to a format which can be displayed. + * + * @internal This is a purely internal method. If you rely on this method, you are advised to + * copy/paste the code as this method may change, or be removed without prior notice. + * + * @param array $params + * @param array $types + * + * @return array + */ + public function resolveParams(array $params, array $types) + { + $resolvedParams = array(); + + // 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 $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$bindIndex] = $value; + } else { + $resolvedParams[$bindIndex] = $value; + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$name] = $value; + } else { + $resolvedParams[$name] = $value; + } + } + } + + return $resolvedParams; + } + + /** + * Creates a new instance of a SQL query builder. + * + * @return \Doctrine\DBAL\Query\QueryBuilder + */ + public function createQueryBuilder() + { + return new Query\QueryBuilder($this); + } + + /** + * Ping the server + * + * When the server is not available the method returns FALSE. + * It is responsibility of the developer to handle this case + * and abort the request or reconnect manually: + * + * @example + * + * if ($conn->ping() === false) { + * $conn->close(); + * $conn->connect(); + * } + * + * It is undefined if the underlying driver attempts to reconnect + * or disconnect when the connection is not available anymore + * as long it returns TRUE when a reconnect succeeded and + * FALSE when the connection was dropped. + * + * @return bool + */ + public function ping() + { + $this->connect(); + + if ($this->_conn instanceof PingableConnection) { + return $this->_conn->ping(); + } + + try { + $this->query($this->getDatabasePlatform()->getDummySelectSQL()); + + return true; + } catch (DBALException $e) { + return false; + } + } +} 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 00000000000..86494b0361a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage . + */ + +namespace Doctrine\DBAL\Connections; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Configuration; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use 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); + + /** + * You can keep the slave connection and then switch back to it + * during the request if you know what you are doing. + * + * @var boolean + */ + protected $keepSlave = false; + + /** + * Creates Master Slave Connection. + * + * @param array $params + * @param \Doctrine\DBAL\Driver $driver + * @param \Doctrine\DBAL\Configuration|null $config + * @param \Doctrine\Common\EventManager|null $eventManager + * + * @throws \InvalidArgumentException + */ + 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']; + } + + $this->keepSlave = isset($params['keepSlave']) ? (bool) $params['keepSlave'] : false; + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Checks if the connection is currently towards the master or not. + * + * @return boolean + */ + public function isConnectedToMaster() + { + return $this->_conn !== null && $this->_conn === $this->connections['master']; + } + + /** + * {@inheritDoc} + */ + public function connect($connectionName = null) + { + $requestedConnectionChange = ($connectionName !== null); + $connectionName = $connectionName ?: 'slave'; + + if ($connectionName !== 'slave' && $connectionName !== 'master') { + throw new \InvalidArgumentException("Invalid option to connect(), only master or slave allowed."); + } + + // If we have a connection open, and this is not an explicit connection + // change request, then abort right here, because we are already done. + // This prevents writes to the slave in case of "keepSlave" option enabled. + if ($this->_conn && !$requestedConnectionChange) { + return false; + } + + $forceMasterAsSlave = false; + + if ($this->getTransactionNestingLevel() > 0) { + $connectionName = 'master'; + $forceMasterAsSlave = true; + } + + if ($this->connections[$connectionName]) { + $this->_conn = $this->connections[$connectionName]; + + if ($forceMasterAsSlave && ! $this->keepSlave) { + $this->connections['slave'] = $this->_conn; + } + + return false; + } + + if ($connectionName === 'master') { + // Set slave connection to master to avoid invalid reads + if ($this->connections['slave'] && ! $this->keepSlave) { + unset($this->connections['slave']); + } + + $this->connections['master'] = $this->_conn = $this->connectTo($connectionName); + + if ( ! $this->keepSlave) { + $this->connections['slave'] = $this->connections['master']; + } + } 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; + } + + /** + * Connects to a specific connection. + * + * @param string $connectionName + * + * @return \Doctrine\DBAL\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); + } + + /** + * @param string $connectionName + * @param array $params + * + * @return mixed + */ + 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'); + + parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->connect('master'); + + parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + $this->connect('master'); + + return parent::rollBack(); + } + + /** + * {@inheritDoc} + */ + public function delete($tableName, array $identifier, array $types = array()) + { + $this->connect('master'); + + return parent::delete($tableName, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function close() + { + unset($this->connections['master']); + unset($this->connections['slave']); + + parent::close(); + + $this->_conn = null; + $this->connections = array('master' => null, 'slave' => null); + } + + /** + * {@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'); + + parent::createSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function releaseSavepoint($savepoint) + { + $this->connect('master'); + + parent::releaseSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function rollbackSavepoint($savepoint) + { + $this->connect('master'); + + parent::rollbackSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + 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 00000000000..d85dec8d76b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php @@ -0,0 +1,247 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Driver\ExceptionConverterDriver; + +class DBALException extends \Exception +{ + /** + * @param string $method + * + * @return \Doctrine\DBAL\DBALException + */ + public static function notSupported($method) + { + return new self("Operation '$method' is not supported by platform."); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidPlatformSpecified() + { + return new self( + "Invalid 'platform' option specified, need to give an instance of ". + "\Doctrine\DBAL\Platforms\AbstractPlatform."); + } + + /** + * Returns a new instance for an invalid specified platform version. + * + * @param string $version The invalid platform version given. + * @param string $expectedFormat The expected platform version format. + * + * @return DBALException + */ + public static function invalidPlatformVersionSpecified($version, $expectedFormat) + { + return new self( + sprintf( + 'Invalid platform version "%s" specified. ' . + 'The platform version has to be specified in the format: "%s".', + $version, + $expectedFormat + ) + ); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidPdoInstance() + { + return new self( + "The 'pdo' option was used in DriverManager::getConnection() but no ". + "instance of PDO was given." + ); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function driverRequired() + { + return new self("The options 'driver' or 'driverClass' are mandatory if no PDO ". + "instance is given to DriverManager::getConnection()."); + } + + /** + * @param string $unknownDriverName + * @param array $knownDrivers + * + * @return \Doctrine\DBAL\DBALException + */ + public static function unknownDriver($unknownDriverName, array $knownDrivers) + { + return new self("The given 'driver' ".$unknownDriverName." is unknown, ". + "Doctrine currently supports only the following drivers: ".implode(", ", $knownDrivers)); + } + + /** + * @param \Doctrine\DBAL\Driver $driver + * @param \Exception $driverEx + * @param string $sql + * @param array $params + * + * @return \Doctrine\DBAL\DBALException + */ + public static function driverExceptionDuringQuery(Driver $driver, \Exception $driverEx, $sql, array $params = array()) + { + $msg = "An exception occurred while executing '".$sql."'"; + if ($params) { + $msg .= " with params " . self::formatParameters($params); + } + $msg .= ":\n\n".$driverEx->getMessage(); + + if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverException) { + return $driver->convertException($msg, $driverEx); + } + + return new self($msg, 0, $driverEx); + } + + /** + * @param \Doctrine\DBAL\Driver $driver + * @param \Exception $driverEx + * + * @return \Doctrine\DBAL\DBALException + */ + public static function driverException(Driver $driver, \Exception $driverEx) + { + $msg = "An exception occured in driver: " . $driverEx->getMessage(); + + if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverException) { + return $driver->convertException($msg, $driverEx); + } + + return new self($msg, 0, $driverEx); + } + + /** + * Returns a human-readable representation of an array of parameters. + * This properly handles binary data by returning a hex representation. + * + * @param array $params + * + * @return string + */ + private static function formatParameters(array $params) + { + return '[' . implode(', ', array_map(function ($param) { + $json = @json_encode($param); + + if (! is_string($json) || $json == 'null' && is_string($param)) { + // JSON encoding failed, this is not a UTF-8 string. + return '"\x' . implode('\x', str_split(bin2hex($param), 2)) . '"'; + } + + return $json; + }, $params)) . ']'; + } + + /** + * @param string $wrapperClass + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidWrapperClass($wrapperClass) + { + return new self("The given 'wrapperClass' ".$wrapperClass." has to be a ". + "subtype of \Doctrine\DBAL\Connection."); + } + + /** + * @param string $driverClass + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidDriverClass($driverClass) + { + return new self("The given 'driverClass' ".$driverClass." has to implement the ". + "\Doctrine\DBAL\Driver interface."); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidTableName($tableName) + { + return new self("Invalid table name specified: ".$tableName); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\DBALException + */ + public static function noColumnsSpecifiedForTable($tableName) + { + return new self("No columns specified for table ".$tableName); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function limitOffsetInvalid() + { + return new self("Invalid Offset in Limit Query, it has to be larger or equal to 0."); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function typeExists($name) + { + return new self('Type '.$name.' already exists.'); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function unknownColumnType($name) + { + return new self('Unknown column type "'.$name.'" requested. Any Doctrine type that you use has ' . + 'to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the ' . + 'known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database ' . + 'introspection then you might have forgot to register all database types for a Doctrine Type. Use ' . + 'AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement ' . + 'Type#getMappedDatabaseTypes(). If the type name is empty you might ' . + 'have a problem with the cache or forgot some mapping information.' + ); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function typeNotFound($name) + { + return new self('Type to be overwritten '.$name.' does not exist.'); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php new file mode 100644 index 00000000000..21d60646640 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php @@ -0,0 +1,75 @@ +. + */ + +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|null $username The username to use when connecting. + * @param string|null $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\Schema\AbstractSchemaManager + */ + public function getSchemaManager(Connection $conn); + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName(); + + /** + * Gets the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * + * @return string The name of the database. + */ + public function getDatabase(Connection $conn); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php new file mode 100644 index 00000000000..baa081690c0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\DB2Platform; +use Doctrine\DBAL\Schema\DB2SchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for IBM DB2 based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractDB2Driver implements Driver +{ + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return $params['dbname']; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new DB2Platform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new DB2SchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php new file mode 100644 index 00000000000..c5eee5a0735 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Abstract base implementation of the {@link DriverException} interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractDriverException extends \Exception implements DriverException +{ + /** + * The driver specific error code. + * + * @var integer|string|null + */ + private $errorCode; + + /** + * The SQLSTATE of the driver. + * + * @var string|null + */ + private $sqlState; + + /** + * Constructor. + * + * @param string $message The driver error message. + * @param string|null $sqlState The SQLSTATE the driver is in at the time the error occured, if any. + * @param integer|string|null $errorCode The driver specific error code if any. + */ + public function __construct($message, $sqlState = null, $errorCode = null) + { + parent::__construct($message); + + $this->errorCode = $errorCode; + $this->sqlState = $sqlState; + } + + /** + * {@inheritdoc} + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php new file mode 100644 index 00000000000..eff79576291 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -0,0 +1,175 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\MySQL57Platform; +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Schema\MySqlSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for MySQL based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html + * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '1050': + return new Exception\TableExistsException($message, $exception); + + case '1051': + case '1146': + return new Exception\TableNotFoundException($message, $exception); + + case '1216': + case '1217': + case '1451': + case '1452': + case '1701': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + + case '1062': + case '1557': + case '1569': + case '1586': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '1054': + case '1166': + case '1611': + return new Exception\InvalidFieldNameException($message, $exception); + + case '1052': + case '1060': + case '1110': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '1064': + case '1149': + case '1287': + case '1341': + case '1342': + case '1343': + case '1344': + case '1382': + case '1479': + case '1541': + case '1554': + case '1626': + return new Exception\SyntaxErrorException($message, $exception); + + case '1044': + case '1045': + case '1046': + case '1049': + case '1095': + case '1142': + case '1143': + case '1227': + case '1370': + case '2002': + case '2005': + return new Exception\ConnectionException($message, $exception); + + case '1048': + case '1121': + case '1138': + case '1171': + case '1252': + case '1263': + case '1566': + return new Exception\NotNullConstraintViolationException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '..' + ); + } + + if (false !== stripos($version, 'mariadb')) { + return $this->getDatabasePlatform(); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + + if (version_compare($version, '5.7', '>=')) { + return new MySQL57Platform(); + } + + return $this->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DATABASE()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new MySqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new MySqlSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php new file mode 100644 index 00000000000..1a0d9777ffc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Schema\OracleSchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Oracle based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractOracleDriver implements Driver, ExceptionConverterDriver +{ + /** + * {@inheritdoc} + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '1': + case '2299': + case '38911': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '904': + return new Exception\InvalidFieldNameException($message, $exception); + + case '918': + case '960': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '923': + return new Exception\SyntaxErrorException($message, $exception); + + case '942': + return new Exception\TableNotFoundException($message, $exception); + + case '955': + return new Exception\TableExistsException($message, $exception); + + case '1017': + case '12545': + return new Exception\ConnectionException($message, $exception); + + case '1400': + return new Exception\NotNullConstraintViolationException($message, $exception); + + case '2266': + case '2291': + case '2292': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return $params['user']; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new OraclePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new OracleSchemaManager($conn); + } + + /** + * Returns an appropriate Easy Connect String for the given parameters. + * + * @param array $params The connection parameters to return the Easy Connect STring for. + * + * @return string + * + * @link http://download.oracle.com/docs/cd/E11882_01/network.112/e10836/naming.htm + */ + protected function getEasyConnectString(array $params) + { + if ( ! empty($params['host'])) { + if ( ! isset($params['port'])) { + $params['port'] = 1521; + } + + $serviceName = $params['dbname']; + + if ( ! empty($params['servicename'])) { + $serviceName = $params['servicename']; + } + + $service = 'SID=' . $serviceName; + $pooled = ''; + $instance = ''; + + if (isset($params['service']) && $params['service'] == true) { + $service = 'SERVICE_NAME=' . $serviceName; + } + + if (isset($params['instancename']) && ! empty($params['instancename'])) { + $instance = '(INSTANCE_NAME = ' . $params['instancename'] . ')'; + } + + if (isset($params['pooled']) && $params['pooled'] == true) { + $pooled = '(SERVER=POOLED)'; + } + + return '(DESCRIPTION=' . + '(ADDRESS=(PROTOCOL=TCP)(HOST=' . $params['host'] . ')(PORT=' . $params['port'] . '))' . + '(CONNECT_DATA=(' . $service . ')' . $instance . $pooled . '))'; + + } + + return isset($params['dbname']) ? $params['dbname'] : ''; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php new file mode 100644 index 00000000000..4ad7cbeef2f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\PostgreSQL91Platform; +use Doctrine\DBAL\Platforms\PostgreSQL92Platform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Schema\PostgreSqlSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for PostgreSQL based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractPostgreSQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getSQLState()) { + case '0A000': + // Foreign key constraint violations during a TRUNCATE operation + // are considered "feature not supported" in PostgreSQL. + if (strpos($exception->getMessage(), 'truncate') !== false) { + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + } + + break; + case '23502': + return new Exception\NotNullConstraintViolationException($message, $exception); + + case '23503': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + + case '23505': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '42601': + return new Exception\SyntaxErrorException($message, $exception); + + case '42702': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '42703': + return new Exception\InvalidFieldNameException($message, $exception); + + case '42P01': + return new Exception\TableNotFoundException($message, $exception); + + case '42P07': + return new Exception\TableExistsException($message, $exception); + + case '7': + // In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code. + // The exception code is always set to 7 here. + // We have to match against the SQLSTATE in the error message in these cases. + if (strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { + return new Exception\ConnectionException($message, $exception); + } + + break; + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '..' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + + switch(true) { + case version_compare($version, '9.2', '>='): + return new PostgreSQL92Platform(); + case version_compare($version, '9.1', '>='): + return new PostgreSQL91Platform(); + default: + return new PostgreSqlPlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return (isset($params['dbname'])) + ? $params['dbname'] + : $conn->query('SELECT CURRENT_DATABASE()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new PostgreSqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new PostgreSqlSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php new file mode 100644 index 00000000000..0527555af4f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php @@ -0,0 +1,141 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SQLAnywhere11Platform; +use Doctrine\DBAL\Platforms\SQLAnywhere12Platform; +use Doctrine\DBAL\Platforms\SQLAnywhere16Platform; +use Doctrine\DBAL\Platforms\SQLAnywherePlatform; +use Doctrine\DBAL\Schema\SQLAnywhereSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SAP Sybase SQL Anywhere based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLAnywhereDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/index.html#sa160/en/saerrors/sqlerror.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '-100': + case '-103': + case '-832': + return new Exception\ConnectionException($message, $exception); + case '-143': + return new Exception\InvalidFieldNameException($message, $exception); + case '-193': + case '-196': + return new Exception\UniqueConstraintViolationException($message, $exception); + case '-194': + case '-198': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + case '-144': + return new Exception\NonUniqueFieldNameException($message, $exception); + case '-184': + case '-195': + return new Exception\NotNullConstraintViolationException($message, $exception); + case '-131': + return new Exception\SyntaxErrorException($message, $exception); + case '-110': + return new Exception\TableExistsException($message, $exception); + case '-141': + case '-1041': + return new Exception\TableNotFoundException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', + $version, + $versionParts + )) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '...' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $buildVersion = isset($versionParts['build']) ? $versionParts['build'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; + + switch(true) { + case version_compare($version, '16', '>='): + return new SQLAnywhere16Platform(); + case version_compare($version, '12', '>='): + return new SQLAnywhere12Platform(); + case version_compare($version, '11', '>='): + return new SQLAnywhere11Platform(); + default: + return new SQLAnywherePlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DB_NAME()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SQLAnywhere12Platform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SQLAnywhereSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php new file mode 100644 index 00000000000..53a4dd52620 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\SQLServer2005Platform; +use Doctrine\DBAL\Platforms\SQLServer2008Platform; +use Doctrine\DBAL\Platforms\SQLServer2012Platform; +use Doctrine\DBAL\Platforms\SQLServerPlatform; +use Doctrine\DBAL\Schema\SQLServerSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Microsoft SQL Server based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLServerDriver implements Driver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', + $version, + $versionParts + )) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '...' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $buildVersion = isset($versionParts['build']) ? $versionParts['build'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; + + switch(true) { + case version_compare($version, '11.00.2100', '>='): + return new SQLServer2012Platform(); + case version_compare($version, '10.00.1600', '>='): + return new SQLServer2008Platform(); + case version_compare($version, '9.00.1399', '>='): + return new SQLServer2005Platform(); + default: + return new SQLServerPlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DB_NAME()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SQLServer2008Platform(); + } + + /** + * {@inheritdoc} + */ + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SQLServerSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php new file mode 100644 index 00000000000..0facc9b8154 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Schema\SqliteSchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver +{ + /** + * {@inheritdoc} + * + * @link http://www.sqlite.org/c3ref/c_abort.html + */ + public function convertException($message, DriverException $exception) + { + if (strpos($exception->getMessage(), 'must be unique') !== false || + strpos($exception->getMessage(), 'is not unique') !== false || + strpos($exception->getMessage(), 'are not unique') !== false || + strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false + ) { + return new Exception\UniqueConstraintViolationException($message, $exception); + } + + if (strpos($exception->getMessage(), 'may not be NULL') !== false || + strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false + ) { + return new Exception\NotNullConstraintViolationException($message, $exception); + } + + if (strpos($exception->getMessage(), 'no such table:') !== false) { + return new Exception\TableNotFoundException($message, $exception); + } + + if (strpos($exception->getMessage(), 'already exists') !== false) { + return new Exception\TableExistsException($message, $exception); + } + + if (strpos($exception->getMessage(), 'has no column named') !== false) { + return new Exception\InvalidFieldNameException($message, $exception); + } + + if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { + return new Exception\NonUniqueFieldNameException($message, $exception); + } + + if (strpos($exception->getMessage(), 'syntax error') !== false) { + return new Exception\SyntaxErrorException($message, $exception); + } + + if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { + return new Exception\ReadOnlyException($message, $exception); + } + + if (strpos($exception->getMessage(), 'unable to open database file') !== false) { + return new Exception\ConnectionException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return isset($params['path']) ? $params['path'] : null; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SqlitePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SqliteSchemaManager($conn); + } +} 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 00000000000..401f2175c89 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php @@ -0,0 +1,110 @@ +. + */ + +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 +{ + /** + * Prepares a statement for execution and returns a Statement object. + * + * @param string $prepareString + * + * @return \Doctrine\DBAL\Driver\Statement + */ + function prepare($prepareString); + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + function query(); + + /** + * Quotes a string for use in a query. + * + * @param string $input + * @param integer $type + * + * @return string + */ + function quote($input, $type=\PDO::PARAM_STR); + + /** + * Executes an SQL statement and return the number of affected rows. + * + * @param string $statement + * + * @return integer + */ + function exec($statement); + + /** + * Returns the ID of the last inserted row or sequence value. + * + * @param string|null $name + * + * @return string + */ + function lastInsertId($name = null); + + /** + * Initiates a transaction. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function beginTransaction(); + + /** + * Commits a transaction. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function commit(); + + /** + * Rolls back the current transaction, as initiated by beginTransaction(). + * + * @return boolean TRUE on success or FALSE on failure. + */ + function rollBack(); + + /** + * Returns the error code associated with the last operation on the database handle. + * + * @return string|null The error code, or null if no operation has been run on the database handle. + */ + function errorCode(); + + /** + * Returns extended error information associated with the last operation on the database handle. + * + * @return array + */ + function errorInfo(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DriverException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DriverException.php new file mode 100644 index 00000000000..f11146b1bfc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DriverException.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a driver exception. + * + * Driver exceptions provide the SQLSTATE of the driver + * and the driver specific error code at the time the error occurred. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface DriverException +{ + /** + * Returns the driver specific error code if available. + * + * Returns null if no driver specific error code is available + * for the error raised by the driver. + * + * @return integer|string|null + */ + public function getErrorCode(); + + /** + * Returns the driver error message. + * + * @return string + */ + public function getMessage(); + + /** + * Returns the SQLSTATE the driver was in at the time the error occurred. + * + * Returns null if the driver does not provide a SQLSTATE for the error occurred. + * + * @return string|null + */ + public function getSQLState(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php new file mode 100644 index 00000000000..92ca6601f43 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +/** + * @author Kim Hemsø Rasmussen + */ +class Connection extends \Doctrine\DBAL\Driver\PDOConnection +{ + /** + * {@inheritdoc} + */ + public function quote($value, $type = \PDO::PARAM_STR) + { + if (\PDO::PARAM_BOOL === $type) { + if ($value) { + return 'true'; + } else { + return 'false'; + } + } + + return parent::quote($value, $type); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php new file mode 100644 index 00000000000..ff4cf3b83d0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +use Doctrine\DBAL\Platforms\DrizzlePlatform; +use Doctrine\DBAL\Schema\DrizzleSchemaManager; + +/** + * Drizzle driver using PDO MySql. + * + * @author Kim Hemsø Rasmussen + */ +class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new Connection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + return $conn; + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + return $this->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new DrizzlePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new DrizzleSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle_pdo_mysql'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php new file mode 100644 index 00000000000..0bd8d516e14 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a driver that is capable of converting DBAL driver exceptions into standardized DBAL driver exceptions. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface ExceptionConverterDriver +{ + /** + * Converts a given DBAL driver exception into a standardized DBAL driver exception. + * + * It evaluates the vendor specific error code and SQLSTATE and transforms + * it into a unified {@link Doctrine\DBAL\Exception\DriverException} subclass. + * + * @param string $message The DBAL exception message to use. + * @param \Doctrine\DBAL\Driver\DriverException $exception The DBAL driver exception to convert. + * + * @return \Doctrine\DBAL\Exception\DriverException An instance of one of the DriverException subclasses. + */ + public function convertException($message, DriverException $exception); +} 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 00000000000..0e809e2f5cf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +class DB2Connection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + private $_conn = null; + + /** + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * + * @throws \Doctrine\DBAL\Driver\IBMDB2\DB2Exception + */ + 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()); + } + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = db2_server_info($this->_conn); + + return $serverInfo->DBMS_VER; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($sql) + { + $stmt = @db2_prepare($this->_conn, $sql); + if ( ! $stmt) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + return new DB2Statement($stmt); + } + + /** + * {@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) + { + $input = db2_escape_string($input); + if ($type == \PDO::PARAM_INT) { + return $input; + } else { + return "'".$input."'"; + } + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return db2_last_insert_id($this->_conn); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_OFF); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (!db2_commit($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (!db2_rollback($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return db2_conn_error($this->_conn); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return array( + 0 => db2_conn_errormsg($this->_conn), + 1 => $this->errorCode(), + ); + } +} 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 00000000000..8ed82e4dda2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\AbstractDB2Driver; + +/** + * IBM DB2 Driver. + * + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Driver extends AbstractDB2Driver +{ + /** + * {@inheritdoc} + */ + 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); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ibm_db2'; + } +} 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 00000000000..72775cbfe70 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Exception extends \Exception +{ +} 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 00000000000..b8de9ea9787 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -0,0 +1,344 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\Statement; + +class DB2Statement implements \IteratorAggregate, Statement +{ + /** + * @var resource + */ + private $_stmt = null; + + /** + * @var array + */ + private $_bindParam = array(); + + /** + * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClass = '\stdClass'; + + /** + * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClassCtorArgs = array(); + + /** + * @var integer + */ + private $_defaultFetchMode = \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, + ); + + /** + * @param resource $stmt + */ + 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, $length = 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($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ? $arg2 : $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + switch ($fetchMode) { + case \PDO::FETCH_BOTH: + return db2_fetch_both($this->_stmt); + case \PDO::FETCH_ASSOC: + return db2_fetch_assoc($this->_stmt); + case \PDO::FETCH_CLASS: + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = isset($args[2]) ? $args[2] : array(); + } + + $result = db2_fetch_object($this->_stmt); + + if ($result instanceof \stdClass) { + $result = $this->castObject($result, $className, $ctorArgs); + } + + return $result; + case \PDO::FETCH_NUM: + return db2_fetch_array($this->_stmt); + case \PDO::FETCH_OBJ: + return db2_fetch_object($this->_stmt); + default: + throw new DB2Exception("Given Fetch-Style " . $fetchMode . " is not supported."); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + + switch ($fetchMode) { + case \PDO::FETCH_CLASS: + while ($row = call_user_func_array(array($this, 'fetch'), func_get_args())) { + $rows[] = $row; + } + break; + case \PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(\PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return (@db2_num_rows($this->_stmt))?:0; + } + + /** + * Casts a stdClass object to the given class name mapping its' properties. + * + * @param \stdClass $sourceObject Object to cast from. + * @param string|object $destinationClass Name of the class or class instance to cast to. + * @param array $ctorArgs Arguments to use for constructing the destination class instance. + * + * @return object + * + * @throws DB2Exception + */ + private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = array()) + { + if ( ! is_string($destinationClass)) { + if ( ! is_object($destinationClass)) { + throw new DB2Exception(sprintf( + 'Destination class has to be of type string or object, %s given.', gettype($destinationClass) + )); + } + } else { + $destinationClass = new \ReflectionClass($destinationClass); + $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); + } + + $sourceReflection = new \ReflectionObject($sourceObject); + $destinationClassReflection = new \ReflectionObject($destinationClass); + /** @var \ReflectionProperty[] $destinationProperties */ + $destinationProperties = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER); + + foreach ($sourceReflection->getProperties() as $sourceProperty) { + $sourceProperty->setAccessible(true); + + $name = $sourceProperty->getName(); + $value = $sourceProperty->getValue($sourceObject); + + // Try to find a case-matching property. + if ($destinationClassReflection->hasProperty($name)) { + $destinationProperty = $destinationClassReflection->getProperty($name); + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + + continue; + } + + $name = strtolower($name); + + // Try to find a property without matching case. + // Fallback for the driver returning either all uppercase or all lowercase column names. + if (isset($destinationProperties[$name])) { + $destinationProperty = $destinationProperties[$name]; + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + + continue; + } + + $destinationClass->$name = $value; + } + + return $destinationClass; + } +} 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 00000000000..8949d2b008f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Doctrine\DBAL\DBALException; + +/** + * @author Kim Hemsø Rasmussen + */ +class Driver extends AbstractMySQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new MysqliConnection($params, $username, $password, $driverOptions); + } catch (MysqliException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'mysqli'; + } +} 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 00000000000..ae3502c1682 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -0,0 +1,266 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Connection as Connection; +use Doctrine\DBAL\Driver\PingableConnection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * @author Kim Hemsø Rasmussen + * @author Till Klampaeckel + */ +class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection +{ + /** + * Name of the option to set connection flags + */ + const OPTION_FLAGS = 'flags'; + + /** + * @var \mysqli + */ + private $_conn; + + /** + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * + * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException + */ + public function __construct(array $params, $username, $password, array $driverOptions = array()) + { + $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port'); + + // Fallback to default MySQL port if not given. + if ( ! $port) { + $port = 3306; + } + + $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket'); + $dbname = isset($params['dbname']) ? $params['dbname'] : null; + + $flags = isset($driverOptions[static::OPTION_FLAGS]) ? $driverOptions[static::OPTION_FLAGS] : null; + + $this->_conn = mysqli_init(); + + $this->setDriverOptions($driverOptions); + + set_error_handler(function () {}); + + if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) { + restore_error_handler(); + + throw new MysqliException($this->_conn->connect_error, @$this->_conn->sqlstate ?: 'HY000', $this->_conn->connect_errno); + } + + restore_error_handler(); + + if (isset($params['charset'])) { + $this->_conn->set_charset($params['charset']); + } + } + + /** + * Retrieves 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 getServerVersion() + { + $majorVersion = floor($this->_conn->server_version / 10000); + $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100); + $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100); + + return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@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) + { + if (false === $this->_conn->query($statement)) { + throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno); + } + + 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; + } + + /** + * Apply the driver options to the connection. + * + * @param array $driverOptions + * + * @throws MysqliException When one of of the options is not supported. + * @throws MysqliException When applying doesn't work - e.g. due to incorrect value. + */ + private function setDriverOptions(array $driverOptions = array()) + { + $supportedDriverOptions = array( + \MYSQLI_OPT_CONNECT_TIMEOUT, + \MYSQLI_OPT_LOCAL_INFILE, + \MYSQLI_INIT_COMMAND, + \MYSQLI_READ_DEFAULT_FILE, + \MYSQLI_READ_DEFAULT_GROUP, + ); + + if (defined('MYSQLI_SERVER_PUBLIC_KEY')) { + $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY; + } + + $exceptionMsg = "%s option '%s' with value '%s'"; + + foreach ($driverOptions as $option => $value) { + + if ($option === static::OPTION_FLAGS) { + continue; + } + + if (!in_array($option, $supportedDriverOptions, true)) { + throw new MysqliException( + sprintf($exceptionMsg, 'Unsupported', $option, $value) + ); + } + + if (@mysqli_options($this->_conn, $option, $value)) { + continue; + } + + $msg = sprintf($exceptionMsg, 'Failed to set', $option, $value); + $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn)); + + throw new MysqliException( + $msg, + $this->_conn->sqlstate, + $this->_conn->errno + ); + } + } + + /** + * Pings the server and re-connects when `mysqli.reconnect = 1` + * + * @return bool + */ + public function ping() + { + return $this->_conn->ping(); + } +} 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 00000000000..23728b113a6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +/** + * Exception thrown in case the mysqli driver errors. + * + * @author Kim Hemsø Rasmussen + * @author Steve Müller + */ +class MysqliException extends AbstractDriverException +{ +} 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 00000000000..edc02dff550 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -0,0 +1,371 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Statement; +use PDO; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliStatement implements \IteratorAggregate, Statement +{ + /** + * @var array + */ + 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. + ); + + /** + * @var \mysqli + */ + protected $_conn; + + /** + * @var \mysqli_stmt + */ + protected $_stmt; + + /** + * @var null|boolean|array + */ + protected $_columnNames; + + /** + * @var null|array + */ + protected $_rowBindedValues; + + /** + * @var array + */ + protected $_bindedValues; + + /** + * @var string + */ + protected $types; + + /** + * Contains ref values for bindValue(). + * + * @var array + */ + protected $_values = array(); + + /** + * @var integer + */ + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param \mysqli $conn + * @param string $prepareString + * + * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException + */ + 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->sqlstate, $this->_conn->errno); + } + + $paramCount = $this->_stmt->param_count; + if (0 < $paramCount) { + $this->types = str_repeat('s', $paramCount); + $this->_bindedValues = array_fill(1, $paramCount, null); + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_bindedValues[$column] =& $variable; + $this->types[$column - 1] = $type; + + 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->types[$param - 1] = $type; + + 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'), array($this->types) + $this->_bindedValues)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + } + } + + if ( ! $this->_stmt->execute()) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + + if (null === $this->_columnNames) { + $meta = $this->_stmt->result_metadata(); + if (false !== $meta) { + // We have a result. + $this->_stmt->store_result(); + + $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->sqlstate, $this->_stmt->errno); + } + } else { + $this->_columnNames = false; + } + } + + return true; + } + + /** + * Binds 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 boolean|array + */ + private function _fetch() + { + $ret = $this->_stmt->fetch(); + + if (true === $ret) { + $values = array(); + foreach ($this->_rowBindedValues as $v) { + $values[] = $v; + } + + return $values; + } + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $values = $this->_fetch(); + if (null === $values) { + return null; + } + + if (false === $values) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + switch ($fetchMode) { + 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 '{$fetchMode}'"); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + $rows = array(); + if (PDO::FETCH_COLUMN == $fetchMode) { + while (($row = $this->fetchColumn()) !== false) { + $rows[] = $row; + } + } else { + while (($row = $this->fetch($fetchMode)) !== null) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (null === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@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, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + 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 00000000000..1aa1b876e2b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractOracleDriver; + +/** + * A Doctrine DBAL driver for the Oracle OCI8 PHP extensions. + * + * @author Roman Borschel + * @since 2.0 + */ +class Driver extends AbstractOracleDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new OCI8Connection( + $username, + $password, + $this->_constructDsn($params), + isset($params['charset']) ? $params['charset'] : null, + isset($params['sessionMode']) ? $params['sessionMode'] : OCI_DEFAULT, + isset($params['persistent']) ? $params['persistent'] : false + ); + } catch (OCI8Exception $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Oracle DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function _constructDsn(array $params) + { + return $this->getEasyConnectString($params); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'oci8'; + } +} 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 00000000000..a919ddaab83 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -0,0 +1,233 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Platforms\OraclePlatform; + +/** + * OCI8 implementation of the Connection interface. + * + * @since 2.0 + */ +class OCI8Connection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + protected $dbh; + + /** + * @var integer + */ + protected $executeMode = OCI_COMMIT_ON_SUCCESS; + + /** + * Creates a Connection to an Oracle Database using oci8 extension. + * + * @param string $username + * @param string $password + * @param string $db + * @param string|null $charset + * @param integer $sessionMode + * @param boolean $persistent + * + * @throws OCI8Exception + */ + public function __construct($username, $password, $db, $charset = null, $sessionMode = OCI_DEFAULT, $persistent = false) + { + if (!defined('OCI_NO_AUTO_COMMIT')) { + define('OCI_NO_AUTO_COMMIT', 0); + } + + $this->dbh = $persistent + ? @oci_pconnect($username, $password, $db, $charset, $sessionMode) + : @oci_connect($username, $password, $db, $charset, $sessionMode); + + if ( ! $this->dbh) { + throw OCI8Exception::fromErrorInfo(oci_error()); + } + } + + /** + * {@inheritdoc} + * + * @throws \UnexpectedValueException if the version string returned by the database server + * does not contain a parsable version number. + */ + public function getServerVersion() + { + if ( ! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', oci_server_version($this->dbh), $version)) { + throw new \UnexpectedValueException( + sprintf( + 'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' . + 'Please report this database version string to the Doctrine team.', + oci_server_version($this->dbh) + ) + ); + } + + return $version[1]; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new OCI8Statement($this->dbh, $prepareString, $this); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + //$fetchMode = $args[1]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + 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") . "'"; + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + if ($name === null) { + return false; + } + + OraclePlatform::assertValidIdentifier($name); + + $sql = 'SELECT ' . $name . '.CURRVAL FROM DUAL'; + $stmt = $this->query($sql); + $result = $stmt->fetch(\PDO::FETCH_ASSOC); + + if ($result === false || !isset($result['CURRVAL'])) { + throw new OCI8Exception("lastInsertId failed: Query was executed but no result was returned."); + } + + return (int) $result['CURRVAL']; + } + + /** + * Returns the current execution mode. + * + * @return integer + */ + public function getExecuteMode() + { + return $this->executeMode; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->executeMode = OCI_NO_AUTO_COMMIT; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (!oci_commit($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + + return true; + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (!oci_rollback($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + + return true; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $error = oci_error($this->dbh); + if ($error !== false) { + $error = $error['code']; + } + + return $error; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..1cd08b8a408 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +class OCI8Exception extends AbstractDriverException +{ + /** + * @param array $error + * + * @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception + */ + public static function fromErrorInfo($error) + { + return new self($error['message'], null, $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 00000000000..3270a0b3936 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -0,0 +1,306 @@ +. + */ + +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 +{ + /** + * @var resource + */ + protected $_dbh; + + /** + * @var resource + */ + protected $_sth; + + /** + * @var \Doctrine\DBAL\Driver\OCI8\OCI8Connection + */ + protected $_conn; + + /** + * @var string + */ + protected static $_PARAM = ':param'; + + /** + * @var array + */ + protected static $fetchModeMap = array( + PDO::FETCH_BOTH => OCI_BOTH, + PDO::FETCH_ASSOC => OCI_ASSOC, + PDO::FETCH_NUM => OCI_NUM, + PDO::FETCH_COLUMN => OCI_NUM, + ); + + /** + * @var integer + */ + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var array + */ + 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. + * @param \Doctrine\DBAL\Driver\OCI8\OCI8Connection $conn + */ + 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; + } + + /** + * Converts 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; + } elseif ($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, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = 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); + } elseif ($length !== null) { + return oci_bind_by_name($this->_sth, $column, $variable, $length); + } + + return oci_bind_by_name($this->_sth, $column, $variable); + } + + /** + * {@inheritdoc} + */ + 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($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + if ( ! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + return oci_fetch_array($this->_sth, self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + if ( ! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + $result = array(); + if (self::$fetchModeMap[$fetchMode] === OCI_BOTH) { + while ($row = $this->fetch($fetchMode)) { + $result[] = $row; + } + } else { + $fetchStructure = OCI_FETCHSTATEMENT_BY_ROW; + if ($fetchMode == PDO::FETCH_COLUMN) { + $fetchStructure = OCI_FETCHSTATEMENT_BY_COLUMN; + } + + oci_fetch_all($this->_sth, $result, 0, -1, + self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS); + + if ($fetchMode == PDO::FETCH_COLUMN) { + $result = $result[0]; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = oci_fetch_array($this->_sth, OCI_NUM | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@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 00000000000..8960de1ae9d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -0,0 +1,133 @@ +. + */ + +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, ServerInfoAwareConnection +{ + /** + * @param string $dsn + * @param string|null $user + * @param string|null $password + * @param array|null $options + * + * @throws PDOException in case of an error. + */ + public function __construct($dsn, $user = null, $password = null, array $options = null) + { + try { + 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); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + try { + return parent::exec($statement); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + return PDO::getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString, $driverOptions = array()) + { + try { + return parent::prepare($prepareString, $driverOptions); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $argsCount = count($args); + + try { + if ($argsCount == 4) { + return parent::query($args[0], $args[1], $args[2], $args[3]); + } + + if ($argsCount == 3) { + return parent::query($args[0], $args[1], $args[2]); + } + + if ($argsCount == 2) { + return parent::query($args[0], $args[1]); + } + + return parent::query($args[0]); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type = \PDO::PARAM_STR) + { + return parent::quote($input, $type); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return parent::lastInsertId($name); + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php new file mode 100644 index 00000000000..7a1a02a43f5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Tiny wrapper for PDOException instances to implement the {@link DriverException} interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PDOException extends \PDOException implements DriverException +{ + /** + * The driver specific error code. + * + * @var integer|string|null + */ + private $errorCode; + + /** + * The SQLSTATE of the driver. + * + * @var string|null + */ + private $sqlState; + + /** + * Constructor. + * + * @param \PDOException $exception The PDO exception to wrap. + */ + public function __construct(\PDOException $exception) + { + parent::__construct($exception->getMessage(), 0, $exception); + + $this->code = $exception->getCode(); + $this->errorInfo = $exception->errorInfo; + $this->errorCode = isset($exception->errorInfo[1]) ? $exception->errorInfo[1] : $exception->getCode(); + $this->sqlState = isset($exception->errorInfo[0]) ? $exception->errorInfo[0] : $exception->getCode(); + } + + /** + * {@inheritdoc} + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} 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 00000000000..0b929f72007 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOIbm; + +use Doctrine\DBAL\Driver\AbstractDB2Driver; +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * Driver for the PDO IBM extension. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Driver extends AbstractDB2Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + return $conn; + } + + /** + * Constructs the IBM PDO DSN. + * + * @param array $params + * + * @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; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_ibm'; + } +} 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 00000000000..7e3632dde91 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOMySql; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use PDOException; + +/** + * PDO MySql driver. + * + * @since 2.0 + */ +class Driver extends AbstractMySQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + $conn = new PDOConnection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (PDOException $e) { + throw DBALException::driverException($this, $e); + } + + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected 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; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_mysql'; + } +} 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 00000000000..6e958c7448e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOOracle; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractOracleDriver; +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * PDO Oracle driver. + * + * WARNING: This driver gives us segfaults 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 extends AbstractOracleDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new PDOConnection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (\PDOException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Oracle PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function constructPdoDsn(array $params) + { + $dsn = 'oci:dbname=' . $this->getEasyConnectString($params); + + if (isset($params['charset'])) { + $dsn .= ';charset=' . $params['charset']; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_oracle'; + } +} 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 00000000000..cc93a9b7e42 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOPgSql; + +use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\DBALException; +use PDOException; +use PDO; + +/** + * Driver that connects through pdo_pgsql. + * + * @since 2.0 + */ +class Driver extends AbstractPostgreSQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + $pdo = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES') + && (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) + || true === $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] + ) + ) { + $pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); + } + + /* defining client_encoding via SET NAMES to avoid inconsistent DSN support + * - the 'client_encoding' connection param only works with postgres >= 9.1 + * - passing client_encoding via the 'options' param breaks pgbouncer support + */ + if (isset($params['charset'])) { + $pdo->query('SET NAMES \''.$params['charset'].'\''); + } + + return $pdo; + } catch (PDOException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Postgres PDO DSN. + * + * @param array $params + * + * @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'] . ' '; + } else { + // Used for temporary connections to allow operations like dropping the database currently connected to. + // Connecting without an explicit database does not work, therefore "template1" database is used + // as it is certainly present in every server setup. + $dsn .= 'dbname=template1' . ' '; + } + + if (isset($params['sslmode'])) { + $dsn .= 'sslmode=' . $params['sslmode'] . ' '; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_pgsql'; + } +} 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 00000000000..3c5382fb5f3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlite; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractSQLiteDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use PDOException; + +/** + * The PDO Sqlite driver. + * + * @since 2.0 + */ +class Driver extends AbstractSQLiteDriver +{ + /** + * @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), + ); + + /** + * {@inheritdoc} + */ + 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']); + } + + try { + $pdo = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (PDOException $ex) { + throw DBALException::driverException($this, $ex); + } + + foreach ($this->_userDefinedFunctions as $fn => $data) { + $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + } + + return $pdo; + } + + /** + * Constructs the Sqlite PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function _constructPdoDsn(array $params) + { + $dsn = 'sqlite:'; + if (isset($params['path'])) { + $dsn .= $params['path']; + } elseif (isset($params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlite'; + } +} 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 00000000000..4c04bfb3e72 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * Sqlsrv Connection implementation. + * + * @since 2.0 + */ +class Connection extends 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; + } +} 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 00000000000..64b91f194c0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +use Doctrine\DBAL\Driver\AbstractSQLServerDriver; + +/** + * The PDO-based Sqlsrv driver. + * + * @since 2.0 + */ +class Driver extends AbstractSQLServerDriver +{ + /** + * {@inheritdoc} + */ + 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. + * + * @param array $params + * + * @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; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlsrv'; + } +} 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 00000000000..b6a8cb48c51 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -0,0 +1,156 @@ +. + */ + +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 +{ + /** + * Protected constructor. + */ + protected function __construct() + { + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $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. + try { + if ($arg2 === null && $arg3 === null) { + return parent::setFetchMode($fetchMode); + } + + if ($arg3 === null) { + return parent::setFetchMode($fetchMode, $arg2); + } + + return parent::setFetchMode($fetchMode, $arg2, $arg3); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = \PDO::PARAM_STR) + { + try { + return parent::bindValue($param, $value, $type); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = \PDO::PARAM_STR, $length = null, $driverOptions = null) + { + try { + return parent::bindParam($column, $variable, $type, $length, $driverOptions); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + try { + return parent::execute($params); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = null, $cursorOffset = null) + { + try { + if ($fetchMode === null && $cursorOrientation === null && $cursorOffset === null) { + return parent::fetch(); + } + + if ($cursorOrientation === null && $cursorOffset === null) { + return parent::fetch($fetchMode); + } + + if ($cursorOffset === null) { + return parent::fetch($fetchMode, $cursorOrientation); + } + + return parent::fetch($fetchMode, $cursorOrientation, $cursorOffset); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + try { + if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) { + return parent::fetchAll(); + } + + if ($fetchArgument === null && $ctorArgs === null) { + return parent::fetchAll($fetchMode); + } + + if ($ctorArgs === null) { + return parent::fetchAll($fetchMode, $fetchArgument); + } + + return parent::fetchAll($fetchMode, $fetchArgument, $ctorArgs); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + try { + return parent::fetchColumn($columnIndex); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php new file mode 100644 index 00000000000..8c85a82c839 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * An interface for connections which support a "native" ping method. + * + * @link www.doctrine-project.org + * @since 2.5 + * @author Till Klampaeckel + * @author Benjamin Eberlei + */ +interface PingableConnection +{ + /** + * Pings the database server to determine if the connection is still + * available. Return true/false based on if that was successful or not. + * + * @return bool + */ + public function ping(); +} 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 00000000000..ad76ddb1164 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * 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 TRUE on success or FALSE on failure. + */ + public function closeCursor(); + + /** + * Returns the number of columns in the result set + * + * @return integer 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(); + + /** + * Sets the fetch mode to use while iterating this statement. + * + * @param integer $fetchMode The fetch mode must be one of the PDO::FETCH_* constants. + * @param mixed $arg2 + * @param mixed $arg3 + * + * @return boolean + * + * @see PDO::FETCH_* constants. + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null); + + /** + * Returns the next row of a result set. + * + * @param integer|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the PDO::FETCH_* constants, + * defaulting to PDO::FETCH_BOTH. + * + * @return mixed The return value of this method on success depends on the fetch mode. In all cases, FALSE is + * returned on failure. + * + * @see PDO::FETCH_* constants. + */ + public function fetch($fetchMode = null); + + /** + * Returns an array containing all of the result set rows. + * + * @param integer|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the PDO::FETCH_* constants, + * defaulting to PDO::FETCH_BOTH. + * + * @return array + * + * @see PDO::FETCH_* constants. + */ + public function fetchAll($fetchMode = null); + + /** + * 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|boolean A single column in the next row of a result set, or FALSE if there are no more rows. + */ + public function fetchColumn($columnIndex = 0); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php new file mode 100644 index 00000000000..cb694a32e20 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractSQLAnywhereDriver; + +/** + * A Doctrine DBAL driver for the SAP Sybase SQL Anywhere PHP extension. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class Driver extends AbstractSQLAnywhereDriver +{ + /** + * {@inheritdoc} + * + * @throws \Doctrine\DBAL\DBALException if there was a problem establishing the connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new SQLAnywhereConnection( + $this->buildDsn( + isset($params['host']) ? $params['host'] : null, + isset($params['port']) ? $params['port'] : null, + isset($params['server']) ? $params['server'] : null, + isset($params['dbname']) ? $params['dbname'] : null, + $username, + $password, + $driverOptions + ), + isset($params['persistent']) ? $params['persistent'] : false + ); + } catch (SQLAnywhereException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlanywhere'; + } + + /** + * Build the connection string for given connection parameters and driver options. + * + * @param string $host Host address to connect to. + * @param integer $port Port to use for the connection (default to SQL Anywhere standard port 2638). + * @param string $server Database server name on the host to connect to. + * SQL Anywhere allows multiple database server instances on the same host, + * therefore specifying the server instance name to use is mandatory. + * @param string $dbname Name of the database on the server instance to connect to. + * @param string $username User name to use for connection authentication. + * @param string $password Password to use for connection authentication. + * @param array $driverOptions Additional parameters to use for the connection. + * + * @return string + */ + private function buildDsn($host, $port, $server, $dbname, $username = null, $password = null, array $driverOptions = array()) + { + $host = $host ?: 'localhost'; + $port = $port ?: 2638; + + if (! empty($server)) { + $server = ';ServerName=' . $server; + } + + return + 'HOST=' . $host . ':' . $port . + $server . + ';DBN=' . $dbname . + ';UID=' . $username . + ';PWD=' . $password . + ';' . implode( + ';', + array_map(function ($key, $value) { + return $key . '=' . $value; + }, array_keys($driverOptions), $driverOptions) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php new file mode 100644 index 00000000000..79fbb363ce7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php @@ -0,0 +1,223 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * SAP Sybase SQL Anywhere implementation of the Connection interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource The SQL Anywhere connection resource. + */ + private $connection; + + /** + * Constructor. + * + * Connects to database with given connection string. + * + * @param string $dsn The connection string. + * @param boolean $persistent Whether or not to establish a persistent connection. + * + * @throws SQLAnywhereException + */ + public function __construct($dsn, $persistent = false) + { + $this->connection = $persistent ? @sasql_pconnect($dsn) : @sasql_connect($dsn); + + if ( ! is_resource($this->connection) || get_resource_type($this->connection) !== 'SQLAnywhere connection') { + throw SQLAnywhereException::fromSQLAnywhereError(); + } + + // Disable PHP warnings on error. + if ( ! sasql_set_option($this->connection, 'verbose_errors', false)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + // Enable auto committing by default. + if ( ! sasql_set_option($this->connection, 'auto_commit', 'on')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + // Enable exact, non-approximated row count retrieval. + if ( ! sasql_set_option($this->connection, 'row_counts', true)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function beginTransaction() + { + if ( ! sasql_set_option($this->connection, 'auto_commit', 'off')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function commit() + { + if ( ! sasql_commit($this->connection)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + $this->endTransaction(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return sasql_errorcode($this->connection); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sasql_error($this->connection); + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + return $this->query("SELECT PROPERTY('ProductVersion')")->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + if (null === $name) { + return sasql_insert_id($this->connection); + } + + return $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new SQLAnywhereStatement($this->connection, $prepareString); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $stmt = $this->prepare($args[0]); + + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type = \PDO::PARAM_STR) + { + if (is_int($input) || is_float($input)) { + return $input; + } + + return "'" . sasql_escape_string($this->connection, $input) . "'"; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function rollBack() + { + if ( ! sasql_rollback($this->connection)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + $this->endTransaction(); + + return true; + } + + /** + * Ends transactional mode and enables auto commit again. + * + * @throws SQLAnywhereException + * + * @return boolean Whether or not ending transactional mode succeeded. + */ + private function endTransaction() + { + if ( ! sasql_set_option($this->connection, 'auto_commit', 'on')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php new file mode 100644 index 00000000000..b68777da112 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +/** + * SAP Sybase SQL Anywhere driver exception. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereException extends AbstractDriverException +{ + /** + * Helper method to turn SQL Anywhere error into exception. + * + * @param resource|null $conn The SQL Anywhere connection resource to retrieve the last error from. + * @param resource|null $stmt The SQL Anywhere statement resource to retrieve the last error from. + * + * @return SQLAnywhereException + * + * @throws \InvalidArgumentException + */ + public static function fromSQLAnywhereError($conn = null, $stmt = null) + { + if (null !== $conn && ! (is_resource($conn) && get_resource_type($conn) === 'SQLAnywhere connection')) { + throw new \InvalidArgumentException('Invalid SQL Anywhere connection resource given: ' . $conn); + } + + if (null !== $stmt && ! (is_resource($stmt) && get_resource_type($stmt) === 'SQLAnywhere statement')) { + throw new \InvalidArgumentException('Invalid SQL Anywhere statement resource given: ' . $stmt); + } + + $state = $conn ? sasql_sqlstate($conn) : sasql_sqlstate(); + $code = null; + $message = null; + + /** + * Try retrieving the last error from statement resource if given + */ + if ($stmt) { + $code = sasql_stmt_errno($stmt); + $message = sasql_stmt_error($stmt); + } + + /** + * Try retrieving the last error from the connection resource + * if either the statement resource is not given or the statement + * resource is given but the last error could not be retrieved from it (fallback). + * Depending on the type of error, it is sometimes necessary to retrieve + * it from the connection resource even though it occurred during + * a prepared statement. + */ + if ($conn && ! $code) { + $code = sasql_errorcode($conn); + $message = sasql_error($conn); + } + + /** + * Fallback mode if either no connection resource is given + * or the last error could not be retrieved from the given + * connection / statement resource. + */ + if ( ! $conn || ! $code) { + $code = sasql_errorcode(); + $message = sasql_error(); + } + + if ($message) { + return new self('SQLSTATE [' . $state . '] [' . $code . '] ' . $message, $state, $code); + } + + return new self('SQL Anywhere error occurred but no error message was retrieved from driver.', $state, $code); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php new file mode 100644 index 00000000000..7a3b120174b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -0,0 +1,347 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use IteratorAggregate; +use PDO; +use Doctrine\DBAL\Driver\Statement; + +/** + * SAP SQL Anywhere implementation of the Statement interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereStatement implements IteratorAggregate, Statement +{ + /** + * @var resource The connection resource. + */ + private $conn; + + /** + * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClass = '\stdClass'; + + /** + * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClassCtorArgs = array(); + + /** + * @var int Default fetch mode to use. + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var resource The result set resource to fetch. + */ + private $result; + + /** + * @var resource The prepared SQL statement to execute. + */ + private $stmt; + + /** + * Constructor. + * + * Prepares given statement for given connection. + * + * @param resource $conn The connection resource to use. + * @param string $sql The SQL statement to prepare. + * + * @throws SQLAnywhereException + */ + public function __construct($conn, $sql) + { + if ( ! is_resource($conn) || get_resource_type($conn) !== 'SQLAnywhere connection') { + throw new SQLAnywhereException('Invalid SQL Anywhere connection resource: ' . $conn); + } + + $this->conn = $conn; + $this->stmt = sasql_prepare($conn, $sql); + + if ( ! is_resource($this->stmt) || get_resource_type($this->stmt) !== 'SQLAnywhere statement') { + throw SQLAnywhereException::fromSQLAnywhereError($conn); + } + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + switch ($type) { + case PDO::PARAM_INT: + case PDO::PARAM_BOOL: + $type = 'i'; + break; + case PDO::PARAM_LOB: + $type = 'b'; + break; + case PDO::PARAM_NULL: + case PDO::PARAM_STR: + $type = 's'; + break; + default: + throw new SQLAnywhereException('Unknown type: ' . $type); + } + + if ( ! sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function closeCursor() + { + if ( ! sasql_stmt_free_result($this->stmt)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return sasql_stmt_field_count($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return sasql_stmt_errno($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sasql_stmt_error($this->stmt); + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function execute($params = null) + { + if (is_array($params)) { + $hasZeroIndex = array_key_exists(0, $params); + + foreach ($params as $key => $val) { + $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key; + + $this->bindValue($key, $val); + } + } + + if ( ! sasql_stmt_execute($this->stmt)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + $this->result = sasql_stmt_result_metadata($this->stmt); + + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function fetch($fetchMode = null) + { + if ( ! is_resource($this->result) || get_resource_type($this->result) !== 'SQLAnywhere result') { + return false; + } + + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + switch ($fetchMode) { + case PDO::FETCH_ASSOC: + return sasql_fetch_assoc($this->result); + case PDO::FETCH_BOTH: + return sasql_fetch_array($this->result, SASQL_BOTH); + case PDO::FETCH_CLASS: + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = isset($args[2]) ? $args[2] : array(); + } + + $result = sasql_fetch_object($this->result); + + if ($result instanceof \stdClass) { + $result = $this->castObject($result, $className, $ctorArgs); + } + + return $result; + case PDO::FETCH_NUM: + return sasql_fetch_row($this->result); + case PDO::FETCH_OBJ: + return sasql_fetch_object($this->result); + default: + throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + + switch ($fetchMode) { + case PDO::FETCH_CLASS: + while ($row = call_user_func_array(array($this, 'fetch'), func_get_args())) { + $rows[] = $row; + } + break; + case PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->fetchAll()); + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sasql_stmt_affected_rows($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ? $arg2 : $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + } + + /** + * Casts a stdClass object to the given class name mapping its' properties. + * + * @param \stdClass $sourceObject Object to cast from. + * @param string|object $destinationClass Name of the class or class instance to cast to. + * @param array $ctorArgs Arguments to use for constructing the destination class instance. + * + * @return object + * + * @throws SQLAnywhereException + */ + private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = array()) + { + if ( ! is_string($destinationClass)) { + if ( ! is_object($destinationClass)) { + throw new SQLAnywhereException(sprintf( + 'Destination class has to be of type string or object, %s given.', gettype($destinationClass) + )); + } + } else { + $destinationClass = new \ReflectionClass($destinationClass); + $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); + } + + $sourceReflection = new \ReflectionObject($sourceObject); + $destinationClassReflection = new \ReflectionObject($destinationClass); + + foreach ($sourceReflection->getProperties() as $sourceProperty) { + $sourceProperty->setAccessible(true); + + $name = $sourceProperty->getName(); + $value = $sourceProperty->getValue($sourceObject); + + if ($destinationClassReflection->hasProperty($name)) { + $destinationProperty = $destinationClassReflection->getProperty($name); + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + } else { + $destinationClass->$name = $value; + } + } + + return $destinationClass; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php new file mode 100644 index 00000000000..f9423973e25 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\Driver\AbstractSQLServerDriver; + +/** + * Driver for ext/sqlsrv. + */ +class Driver extends AbstractSQLServerDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if (!isset($params['host'])) { + throw new SQLSrvException("Missing 'host' in configuration for sqlsrv driver."); + } + + $serverName = $params['host']; + if (isset($params['port'])) { + $serverName .= ', ' . $params['port']; + } + + if (isset($params['dbname'])) { + $driverOptions['Database'] = $params['dbname']; + } + + if (isset($params['charset'])) { + $driverOptions['CharacterSet'] = $params['charset']; + } + + $driverOptions['UID'] = $username; + $driverOptions['PWD'] = $password; + + if (!isset($driverOptions['ReturnDatesAsStrings'])) { + $driverOptions['ReturnDatesAsStrings'] = 1; + } + + return new SQLSrvConnection($serverName, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlsrv'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php new file mode 100644 index 00000000000..67c7bfa851a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * Last Id Data Container. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class LastInsertId +{ + /** + * @var integer + */ + private $id; + + /** + * @param integer $id + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php new file mode 100644 index 00000000000..514ae54fc54 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php @@ -0,0 +1,193 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * SQL Server implementation for the Connection interface. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvConnection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + protected $conn; + + /** + * @var \Doctrine\DBAL\Driver\SQLSrv\LastInsertId + */ + protected $lastInsertId; + + /** + * @param string $serverName + * @param array $connectionOptions + * + * @throws \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException + */ + public function __construct($serverName, $connectionOptions) + { + if ( ! sqlsrv_configure('WarningsReturnAsErrors', 0)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + $this->conn = sqlsrv_connect($serverName, $connectionOptions); + if ( ! $this->conn) { + throw SQLSrvException::fromSqlSrvErrors(); + } + $this->lastInsertId = new LastInsertId(); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = sqlsrv_server_info($this->conn); + + return $serverInfo['SQLServerVersion']; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function prepare($sql) + { + return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId); + } + + /** + * {@inheritDoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritDoc} + * @license New BSD, code from Zend Framework + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value)) { + return $value; + } elseif (is_float($value)) { + return sprintf('%F', $value); + } + + return "'" . str_replace("'", "''", $value) . "'"; + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if ($name !== null) { + $sql = "SELECT IDENT_CURRENT(".$this->quote($name).") AS LastInsertId"; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt->fetchColumn(); + } + + return $this->lastInsertId->getId(); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + if ( ! sqlsrv_begin_transaction($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function commit() + { + if ( ! sqlsrv_commit($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + if ( ! sqlsrv_rollback($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php new file mode 100644 index 00000000000..50705adabbb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\DBALException; + +class SQLSrvException extends DBALException +{ + /** + * Helper method to turn sql server errors into exception. + * + * @return \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException + */ + static public function fromSqlSrvErrors() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + $message = ""; + foreach ($errors as $error) { + $message .= "SQLSTATE [".$error['SQLSTATE'].", ".$error['code']."]: ". $error['message']."\n"; + } + if ( ! $message) { + $message = "SQL Server error occurred but no error message was retrieved from driver."; + } + + return new self(rtrim($message)); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php new file mode 100644 index 00000000000..ead250c1857 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -0,0 +1,309 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * SQL Server Statement. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvStatement implements IteratorAggregate, Statement +{ + /** + * The SQLSRV Resource. + * + * @var resource + */ + private $conn; + + /** + * The SQL statement to execute. + * + * @var string + */ + private $sql; + + /** + * The SQLSRV statement resource. + * + * @var resource + */ + private $stmt; + + /** + * Parameters to bind. + * + * @var array + */ + private $params = array(); + + /** + * Translations. + * + * @var array + */ + private static $fetchMap = array( + PDO::FETCH_BOTH => SQLSRV_FETCH_BOTH, + PDO::FETCH_ASSOC => SQLSRV_FETCH_ASSOC, + PDO::FETCH_NUM => SQLSRV_FETCH_NUMERIC, + ); + + /** + * The name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + * + * @var string + */ + private $defaultFetchClass = '\stdClass'; + + /** + * The constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + * + * @var string + */ + private $defaultFetchClassCtorArgs = array(); + + /** + * The fetch style. + * + * @param integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * The last insert ID. + * + * @var \Doctrine\DBAL\Driver\SQLSrv\LastInsertId|null + */ + private $lastInsertId; + + /** + * Append to any INSERT query to retrieve the last insert id. + * + * @var string + */ + const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; + + /** + * @param resource $conn + * @param string $sql + * @param \Doctrine\DBAL\Driver\SQLSrv\LastInsertId|null $lastInsertId + */ + public function __construct($conn, $sql, LastInsertId $lastInsertId = null) + { + $this->conn = $conn; + $this->sql = $sql; + + if (stripos($sql, 'INSERT INTO ') === 0) { + $this->sql .= self::LAST_INSERT_ID_SQL; + $this->lastInsertId = $lastInsertId; + } + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (!is_numeric($column)) { + throw new SQLSrvException("sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead."); + } + + if ($type === \PDO::PARAM_LOB) { + $this->params[$column-1] = array($variable, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY('max')); + } else { + $this->params[$column-1] = $variable; + } + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + if ($this->stmt) { + sqlsrv_free_stmt($this->stmt); + } + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return sqlsrv_num_fields($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key; + $this->bindValue($key, $val); + } + } + + $this->stmt = sqlsrv_query($this->conn, $this->sql, $this->params); + if ( ! $this->stmt) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + if ($this->lastInsertId) { + sqlsrv_next_result($this->stmt); + sqlsrv_fetch($this->stmt); + $this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0)); + } + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $args = func_get_args(); + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if (isset(self::$fetchMap[$fetchMode])) { + return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]) ?: false; + } + + if ($fetchMode == PDO::FETCH_OBJ || $fetchMode == PDO::FETCH_CLASS) { + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (count($args) >= 2) { + $className = $args[1]; + $ctorArgs = (isset($args[2])) ? $args[2] : array(); + } + + return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs) ?: false; + } + + throw new SQLSrvException("Fetch mode is not supported!"); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + + switch ($fetchMode) { + case PDO::FETCH_CLASS: + while ($row = call_user_func_array(array($this, 'fetch'), func_get_args())) { + $rows[] = $row; + } + break; + case PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sqlsrv_rows_affected($this->stmt); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php new file mode 100644 index 00000000000..f15ffefcbac --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a connection that is able to provide information about the server it is connected to. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface ServerInfoAwareConnection +{ + /** + * Returns the version number of the database server connected to. + * + * @return string + */ + public function getServerVersion(); + + /** + * Checks whether a query is required to retrieve the database server version. + * + * @return boolean True if a query is required to retrieve the database server version, false otherwise. + */ + public function requiresQueryForServerVersion(); +} 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 00000000000..aedf47b8a96 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php @@ -0,0 +1,128 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Statement interface. + * Drivers must implement this interface. + * + * This resembles (a subset of) the PDOStatement interface. + * + * @author Konsta Vesterinen + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +interface Statement extends ResultStatement +{ + /** + * Binds a value to a corresponding named (not supported by mysqli driver, see comment below) or positional + * placeholder in the SQL statement that was used to prepare the statement. + * + * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), + * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. + * + * @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 TRUE on success or FALSE on failure. + */ + function bindValue($param, $value, $type = null); + + + /** + * Binds a PHP variable to a corresponding named (not supported by mysqli driver, see comment below) 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. + * + * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), + * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. + * + * 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 $column 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|null $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. + * @param integer|null $length You must specify maxlength when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function bindParam($column, &$variable, $type = null, $length = null); + + /** + * Fetches the SQLSTATE associated with the last operation on the statement handle. + * + * @see Doctrine_Adapter_Interface::errorCode() + * + * @return string The error code string. + */ + function errorCode(); + + /** + * Fetches extended error information associated with the last operation on the statement handle. + * + * @see Doctrine_Adapter_Interface::errorInfo() + * + * @return array The 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|null $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function execute($params = null); + + /** + * 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 The number of rows. + */ + function rowCount(); +} 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 00000000000..75004a40718 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php @@ -0,0 +1,276 @@ +. + */ + +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. + * + * To add your own driver use the 'driverClass' parameter to + * {@link DriverManager::getConnection()}. + * + * @var array + */ + 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_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver', + 'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver', + 'drizzle_pdo_mysql' => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver', + 'sqlanywhere' => 'Doctrine\DBAL\Driver\SQLAnywhere\Driver', + 'sqlsrv' => 'Doctrine\DBAL\Driver\SQLSrv\Driver', + ); + + /** + * List of URL schemes from a database URL and their mappings to driver. + */ + private static $driverSchemeAliases = array( + 'db2' => 'ibm_db2', + 'mssql' => 'pdo_sqlsrv', + 'mysql' => 'pdo_mysql', + 'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason + 'postgres' => 'pdo_pgsql', + 'postgresql' => 'pdo_pgsql', + 'pgsql' => 'pdo_pgsql', + 'sqlite' => 'pdo_sqlite', + 'sqlite3' => 'pdo_sqlite', + ); + + /** + * 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_oci (unstable) + * pdo_sqlsrv + * pdo_sqlsrv + * mysqli + * sqlanywhere + * sqlsrv + * ibm_db2 (unstable) + * drizzle_pdo_mysql + * + * 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|null $config The configuration to use. + * @param \Doctrine\Common\EventManager|null $eventManager The event manager to use. + * + * @return \Doctrine\DBAL\Connection + * + * @throws \Doctrine\DBAL\DBALException + */ + 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(); + } + + $params = self::parseDatabaseUrl($params); + + // check for existing pdo object + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { + throw DBALException::invalidPdoInstance(); + } elseif (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); + } + + /** + * Returns the list of supported drivers. + * + * @return array + */ + public static function getAvailableDrivers() + { + return array_keys(self::$_driverMap); + } + + /** + * Checks the list of parameters. + * + * @param array $params The list of parameters. + * + * @return void + * + * @throws \Doctrine\DBAL\DBALException + */ + private static function _checkParams(array $params) + { + // check existence 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']); + } + } + + /** + * Extracts parts from a database URL, if present, and returns an + * updated list of parameters. + * + * @param array $params The list of parameters. + * + * @param array A modified list of parameters with info from a database + * URL extracted into indidivual parameter parts. + * + */ + private static function parseDatabaseUrl(array $params) + { + if (!isset($params['url'])) { + return $params; + } + + // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid + $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']); + + $url = parse_url($url); + + if ($url === false) { + throw new DBALException('Malformed parameter "url".'); + } + + if (isset($url['scheme'])) { + $params['driver'] = str_replace('-', '_', $url['scheme']); // URL schemes must not contain underscores, but dashes are ok + if (isset(self::$driverSchemeAliases[$params['driver']])) { + $params['driver'] = self::$driverSchemeAliases[$params['driver']]; // use alias like "postgres", else we just let checkParams decide later if the driver exists (for literal "pdo-pgsql" etc) + } + } + + if (isset($url['host'])) { + $params['host'] = $url['host']; + } + if (isset($url['port'])) { + $params['port'] = $url['port']; + } + if (isset($url['user'])) { + $params['user'] = $url['user']; + } + if (isset($url['pass'])) { + $params['password'] = $url['pass']; + } + + if (isset($url['path'])) { + if (!isset($url['scheme']) || (strpos($url['scheme'], 'sqlite') !== false && $url['path'] == ':memory:')) { + $params['dbname'] = $url['path']; // if the URL was just "sqlite::memory:", which parses to scheme and path only + } else { + $params['dbname'] = substr($url['path'], 1); // strip the leading slash from the URL + } + } + + if (isset($url['query'])) { + $query = array(); + parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode + $params = array_merge($params, $query); // parse_str wipes existing array elements + } + + return $params; + } +} 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 00000000000..fd82638e771 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\DBAL\Connection; + +/** + * Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class ConnectionEventArgs extends EventArgs +{ + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param \Doctrine\DBAL\Connection $connection + */ + 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 00000000000..bd362c4cb67 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php @@ -0,0 +1,80 @@ +. + */ + +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. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @deprecated Use "charset" option to PDO MySQL Connection instead. + */ +class MysqlSessionInit implements EventSubscriber +{ + /** + * The charset. + * + * @var string + */ + private $_charset; + + /** + * The collation, or FALSE if no collation. + * + * @var string|boolean + */ + private $_collation; + + /** + * Configure Charset and Collation options of MySQL Client for each Connection. + * + * @param string $charset The charset. + * @param string|boolean $collation The collation, or FALSE if no collation. + */ + public function __construct($charset = 'utf8', $collation = false) + { + $this->_charset = $charset; + $this->_collation = $collation; + } + + /** + * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $collation = ($this->_collation) ? " COLLATE ".$this->_collation : ""; + $args->getConnection()->executeUpdate("SET NAMES ".$this->_charset . $collation); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} 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 00000000000..e19540eb67d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php @@ -0,0 +1,86 @@ +. + */ + +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 environment does not match the Doctrine requirements. + * + * The following environment 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" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class OracleSessionInit implements EventSubscriber +{ + /** + * @var array + */ + 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", + 'NLS_NUMERIC_CHARACTERS' => ".,", + ); + + /** + * @param array $oracleSessionVars + */ + public function __construct(array $oracleSessionVars = array()) + { + $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); + } + + /** + * @param \Doctrine\DBAL\Event\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); + } + } + + /** + * {@inheritdoc} + */ + 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 00000000000..cfa42994cb8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php @@ -0,0 +1,66 @@ +. + */ + +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. + * + * @link www.doctrine-project.org + * @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 \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $conn = $args->getConnection(); + $conn->exec($this->sql); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} 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 00000000000..c4477ad8a1e --- /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; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for adding table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @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 00000000000..7249b63cb66 --- /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; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for changing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\ColumnDiff + */ + private $_columnDiff; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @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 00000000000..384315d5a09 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @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 00000000000..06f37888ff3 --- /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; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for removing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @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 00000000000..94a274c4fae --- /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; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for renaming table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs +{ + /** + * @var string + */ + private $_oldColumnName; + + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @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 00000000000..14bb8df73b3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Column; + +/** + * Event Arguments used when the portable column definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaColumnDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column|null + */ + private $_column = null; + + /** + * Raw column data as fetched from the database. + * + * @var array + */ + private $_tableColumn; + + /** + * @var string + */ + private $_table; + + /** + * @var string + */ + private $_database; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param array $tableColumn + * @param string $table + * @param string $database + * @param \Doctrine\DBAL\Connection $connection + */ + 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 \Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs + */ + public function setColumn(Column $column = null) + { + $this->_column = $column; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Column|null + */ + 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 00000000000..38b218257b9 --- /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; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating table columns are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @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 00000000000..175b0e9c345 --- /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; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var array + */ + private $_columns; + + /** + * @var array + */ + private $_options; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @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 00000000000..6ffb6442c07 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when the SQL query for dropping tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaDropTableEventArgs extends SchemaEventArgs +{ + /** + * @var string|\Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var string|null + */ + private $_sql = null; + + /** + * @param string|\Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @throws \InvalidArgumentException + */ + public function __construct($table, AbstractPlatform $platform) + { + if ( ! $table instanceof Table && !is_string($table)) { + throw new \InvalidArgumentException('SchemaDropTableEventArgs 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|null + */ + 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 00000000000..d9648b64ffc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; + +/** + * Base class for schema related events. + * + * @link www.doctrine-project.org + * @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 00000000000..3fd1d4bf7da --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Index; + +/** + * Event Arguments used when the portable index definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaIndexDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Index|null + */ + private $_index = null; + + /** + * Raw index data as fetched from the database. + * + * @var array + */ + private $_tableIndex; + + /** + * @var string + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param array $tableIndex + * @param string $table + * @param \Doctrine\DBAL\Connection $connection + */ + 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|null + */ + 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 00000000000..0d31504c70a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Container for all DBAL events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + /** + * Private constructor. This class cannot be instantiated. + */ + 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/Exception/ConnectionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConnectionException.php new file mode 100644 index 00000000000..8d976dcb325 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConnectionException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all connection related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ConnectionException extends DriverException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConstraintViolationException.php new file mode 100644 index 00000000000..8dd1cb69452 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConstraintViolationException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all constraint violation related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ConstraintViolationException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php new file mode 100644 index 00000000000..279aae9a5a3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all already existing database object related errors detected in the driver. + * + * A database object is considered any asset that can be created in a database + * such as schemas, tables, views, sequences, triggers, constraints, indexes, + * functions, stored procedures etc. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DatabaseObjectExistsException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php new file mode 100644 index 00000000000..fbca22d9942 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all unknown database object related errors detected in the driver. + * + * A database object is considered any asset that can be created in a database + * such as schemas, tables, views, sequences, triggers, constraints, indexes, + * functions, stored procedures etc. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DatabaseObjectNotFoundException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DriverException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DriverException.php new file mode 100644 index 00000000000..17c9f3a845a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DriverException.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +use Doctrine\DBAL\DBALException; + +/** + * Base class for all errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DriverException extends DBALException +{ + /** + * The previous DBAL driver exception. + * + * @var \Doctrine\DBAL\Driver\DriverException + */ + private $driverException; + + /** + * Constructor. + * + * @param string $message The exception message. + * @param \Doctrine\DBAL\Driver\DriverException $driverException The DBAL driver exception to chain. + */ + public function __construct($message, \Doctrine\DBAL\Driver\DriverException $driverException) + { + $exception = null; + + if ($driverException instanceof \Exception) { + $exception = $driverException; + } + + parent::__construct($message, 0, $exception); + + $this->driverException = $driverException; + } + + /** + * Returns the driver specific error code if given. + * + * Returns null if no error code was given by the driver. + * + * @return integer|string|null + */ + public function getErrorCode() + { + return $this->driverException->getErrorCode(); + } + + /** + * Returns the SQLSTATE the driver was in at the time the error occurred, if given. + * + * Returns null if no SQLSTATE was given by the driver. + * + * @return string|null + */ + public function getSQLState() + { + return $this->driverException->getSQLState(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php new file mode 100644 index 00000000000..d244855a16d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a foreign key constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ForeignKeyConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php new file mode 100644 index 00000000000..5b811227bdd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +use Doctrine\DBAL\DBALException; + +/** + * Exception to be thrown when invalid arguments are passed to any DBAL API + * + * @author Marco Pivetta + * @link www.doctrine-project.org + * @since 2.5 + */ +class InvalidArgumentException extends DBALException +{ + /** + * @return self + */ + public static function fromEmptyCriteria() + { + return new self('Empty criteria was used, expected non-empty criteria'); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php new file mode 100644 index 00000000000..e0b1013fe4c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an invalid specified field name in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class InvalidFieldNameException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php new file mode 100644 index 00000000000..7b214e3b946 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a non-unique/ambiguous specified field name in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class NonUniqueFieldNameException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php new file mode 100644 index 00000000000..a1f45b92a44 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a NOT NULL constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class NotNullConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ReadOnlyException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ReadOnlyException.php new file mode 100644 index 00000000000..0345e88fce0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ReadOnlyException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a write operation attempt on a read-only database element detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ReadOnlyException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ServerException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ServerException.php new file mode 100644 index 00000000000..36460ee0d64 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ServerException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all server related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ServerException extends DriverException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/SyntaxErrorException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/SyntaxErrorException.php new file mode 100644 index 00000000000..97b8fd25f3e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/SyntaxErrorException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a syntax error in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SyntaxErrorException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableExistsException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableExistsException.php new file mode 100644 index 00000000000..c90bfb476c4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableExistsException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an already existing table referenced in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class TableExistsException extends DatabaseObjectExistsException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableNotFoundException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableNotFoundException.php new file mode 100644 index 00000000000..90793b909de --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableNotFoundException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an unknown table referenced in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class TableNotFoundException extends DatabaseObjectNotFoundException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php new file mode 100644 index 00000000000..cd117738dd9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a unique constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class UniqueConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php new file mode 100644 index 00000000000..62d6fcb8ef1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Connection; + +/** + * Table ID Generator for those poor languages that are missing sequences. + * + * WARNING: The Table Id Generator clones a second independent database + * connection to work correctly. This means using the generator requests that + * generate IDs will have two open database connections. This is necessary to + * be safe from transaction failures in the main connection. Make sure to only + * ever use one TableGenerator otherwise you end up with many connections. + * + * TableID Generator does not work with SQLite. + * + * The TableGenerator does not take care of creating the SQL Table itself. You + * should look at the `TableGeneratorSchemaVisitor` to do this for you. + * Otherwise the schema for a table looks like: + * + * CREATE sequences ( + * sequence_name VARCHAR(255) NOT NULL, + * sequence_value INT NOT NULL DEFAULT '1', + * sequence_increment_by INT NOT NULL DEFAULT '1', + * PRIMARY KEY (table_name) + * ); + * + * Technically this generator works as follows: + * + * 1. Use a robust transaction serialization level. + * 2. Open transaction + * 3. Acquire a read lock on the table row (SELECT .. FOR UPDATE) + * 4. Increment current value by one and write back to database + * 5. Commit transaction + * + * If you are using a sequence_increment_by value that is larger than one the + * ID Generator will keep incrementing values until it hits the incrementation + * gap before issuing another query. + * + * If no row is present for a given sequence a new one will be created with the + * default values 'value' = 1 and 'increment_by' = 1 + * + * @author Benjamin Eberlei + */ +class TableGenerator +{ + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var string + */ + private $generatorTableName; + + /** + * @var array + */ + private $sequences = array(); + + /** + * @param \Doctrine\DBAL\Connection $conn + * @param string $generatorTableName + * + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct(Connection $conn, $generatorTableName = 'sequences') + { + $params = $conn->getParams(); + if ($params['driver'] == 'pdo_sqlite') { + throw new \Doctrine\DBAL\DBALException("Cannot use TableGenerator with SQLite."); + } + $this->conn = DriverManager::getConnection($params, $conn->getConfiguration(), $conn->getEventManager()); + $this->generatorTableName = $generatorTableName; + } + + /** + * Generates the next unused value for the given sequence name. + * + * @param string $sequenceName + * + * @return integer + * + * @throws \Doctrine\DBAL\DBALException + */ + public function nextValue($sequenceName) + { + if (isset($this->sequences[$sequenceName])) { + $value = $this->sequences[$sequenceName]['value']; + $this->sequences[$sequenceName]['value']++; + if ($this->sequences[$sequenceName]['value'] >= $this->sequences[$sequenceName]['max']) { + unset ($this->sequences[$sequenceName]); + } + + return $value; + } + + $this->conn->beginTransaction(); + + try { + $platform = $this->conn->getDatabasePlatform(); + $sql = "SELECT sequence_value, sequence_increment_by " . + "FROM " . $platform->appendLockHint($this->generatorTableName, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) . " " . + "WHERE sequence_name = ? " . $platform->getWriteLockSQL(); + $stmt = $this->conn->executeQuery($sql, array($sequenceName)); + + if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $row = array_change_key_case($row, CASE_LOWER); + + $value = $row['sequence_value']; + $value++; + + if ($row['sequence_increment_by'] > 1) { + $this->sequences[$sequenceName] = array( + 'value' => $value, + 'max' => $row['sequence_value'] + $row['sequence_increment_by'] + ); + } + + $sql = "UPDATE " . $this->generatorTableName . " ". + "SET sequence_value = sequence_value + sequence_increment_by " . + "WHERE sequence_name = ? AND sequence_value = ?"; + $rows = $this->conn->executeUpdate($sql, array($sequenceName, $row['sequence_value'])); + + if ($rows != 1) { + throw new \Doctrine\DBAL\DBALException("Race-condition detected while updating sequence. Aborting generation"); + } + } else { + $this->conn->insert( + $this->generatorTableName, + array('sequence_name' => $sequenceName, 'sequence_value' => 1, 'sequence_increment_by' => 1) + ); + $value = 1; + } + + $this->conn->commit(); + + } catch (\Exception $e) { + $this->conn->rollback(); + throw new \Doctrine\DBAL\DBALException("Error occurred while generating ID with TableGenerator, aborted generation: " . $e->getMessage(), 0, $e); + } + + return $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php new file mode 100644 index 00000000000..25b06aca9f2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +class TableGeneratorSchemaVisitor implements \Doctrine\DBAL\Schema\Visitor\Visitor +{ + /** + * @var string + */ + private $generatorTableName; + + /** + * @param string $generatorTableName + */ + public function __construct($generatorTableName = 'sequences') + { + $this->generatorTableName = $generatorTableName; + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $table = $schema->createTable($this->generatorTableName); + $table->addColumn('sequence_name', 'string'); + $table->addColumn('sequence_value', 'integer', array('default' => 1)); + $table->addColumn('sequence_increment_by', 'integer', array('default' => 1)); + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } +} 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 00000000000..8d78180ede2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Contains all DBAL LockModes. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class LockMode +{ + const NONE = 0; + const OPTIMISTIC = 1; + const PESSIMISTIC_READ = 2; + const PESSIMISTIC_WRITE = 4; + + /** + * Private constructor. This class cannot be instantiated. + */ + final private function __construct() + { + } +} 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 00000000000..b143a3ee28e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Includes executed SQLs in a Debug Stack. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DebugStack implements SQLLogger +{ + /** + * Executed SQL queries. + * + * @var array + */ + public $queries = array(); + + /** + * If Debug Stack is enabled (log queries) or not. + * + * @var boolean + */ + public $enabled = true; + + /** + * @var float|null + */ + public $start = null; + + /** + * @var integer + */ + 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() + { + if ($this->enabled) { + $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 00000000000..8ed04788fd6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * A SQL logger that logs to the standard output using echo/var_dump. + * + * @link www.doctrine-project.org + * @since 2.0 + * @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() + { + } +} 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 00000000000..81845b1b250 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Chains multiple SQLLogger. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Christophe Coevoet + */ +class LoggerChain implements SQLLogger +{ + /** + * @var \Doctrine\DBAL\Logging\SQLLogger[] + */ + private $loggers = array(); + + /** + * Adds a logger in the chain. + * + * @param \Doctrine\DBAL\Logging\SQLLogger $logger + * + * @return void + */ + 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 00000000000..52491e50fe6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Interface for SQL loggers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @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|null $params The SQL parameters. + * @param array|null $types The SQL parameter types. + * + * @return void + */ + public function startQuery($sql, array $params = null, array $types = null); + + /** + * Marks the last started query as stopped. This can be used for timing of queries. + * + * @return void + */ + public function stopQuery(); +} 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 00000000000..f0ace993ea4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -0,0 +1,3528 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Types; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Event\SchemaCreateTableEventArgs; +use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs; +use Doctrine\DBAL\Event\SchemaDropTableEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs; +use 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. + * + * @link www.doctrine-project.org + * @since 2.0 + * @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 integer + */ + const CREATE_INDEXES = 1; + + /** + * @var integer + */ + const CREATE_FOREIGNKEYS = 2; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_SECOND = 'SECOND'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_MINUTE = 'MINUTE'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_HOUR = 'HOUR'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_DAY = 'DAY'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_WEEK = 'WEEK'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_MONTH = 'MONTH'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_QUARTER = 'QUARTER'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_YEAR = 'YEAR'; + + /** + * @var integer + */ + const TRIM_UNSPECIFIED = 0; + + /** + * @var integer + */ + const TRIM_LEADING = 1; + + /** + * @var integer + */ + const TRIM_TRAILING = 2; + + /** + * @var integer + */ + const TRIM_BOTH = 3; + + /** + * @var array|null + */ + protected $doctrineTypeMapping = null; + + /** + * Contains a list of all columns that should generate parseable column comments for type-detection + * in reverse engineering scenarios. + * + * @var array|null + */ + protected $doctrineTypeComments = null; + + /** + * @var \Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * Holds the KeywordList instance for the current platform. + * + * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList + */ + protected $_keywords; + + /** + * Constructor. + */ + public function __construct() + { + } + + /** + * Sets the EventManager used by the Platform. + * + * @param \Doctrine\Common\EventManager $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; + } + + /** + * Returns the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBooleanTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBigIntTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getSmallIntTypeDeclarationSQL(array $columnDef); + + /** + * Returns 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(); + + /** + * Initializes Doctrine Type Mappings with the platform defaults + * and with all additional type mappings. + * + * @return void + */ + private function initializeAllDoctrineTypeMappings() + { + $this->initializeDoctrineTypeMappings(); + + foreach (Type::getTypesMap() as $typeName => $className) { + foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) { + $this->doctrineTypeMapping[$dbType] = $typeName; + } + } + } + + /** + * Returns the SQL snippet used to declare a VARCHAR column type. + * + * @param array $field + * + * @return string + */ + 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); + } + + return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed); + } + + /** + * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. + * + * @param array $field The column definition. + * + * @return string + */ + public function getBinaryTypeDeclarationSQL(array $field) + { + if ( ! isset($field['length'])) { + $field['length'] = $this->getBinaryDefaultLength(); + } + + $fixed = isset($field['fixed']) ? $field['fixed'] : false; + + if ($field['length'] > $this->getBinaryMaxLength()) { + return $this->getBlobTypeDeclarationSQL($field); + } + + return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed); + } + + /** + * Returns the SQL snippet to declare a GUID/UUID field. + * + * By default this maps directly to a CHAR(36) and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getGuidTypeDeclarationSQL(array $field) + { + $field['length'] = 36; + $field['fixed'] = true; + + return $this->getVarcharTypeDeclarationSQL($field); + } + + /** + * Returns the SQL snippet to declare a JSON field. + * + * By default this maps directly to a CLOB and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return $this->getClobTypeDeclarationSQL($field); + } + + /** + * @param integer $length + * @param boolean $fixed + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('VARCHARs not supported by Platform.'); + } + + /** + * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. + * + * @param integer $length The length of the column. + * @param boolean $fixed Whether the column length is fixed. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); + } + + /** + * Returns the SQL snippet used to declare a CLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getClobTypeDeclarationSQL(array $field); + + /** + * Returns the SQL Snippet used to declare a BLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getBlobTypeDeclarationSQL(array $field); + + /** + * Gets the name of the platform. + * + * @return string + */ + abstract public function getName(); + + /** + * Registers a doctrine type to be used in conjunction with a column type of this platform. + * + * @param string $dbType + * @param string $doctrineType + * + * @throws \Doctrine\DBAL\DBALException If the type is not found. + */ + public function registerDoctrineTypeMapping($dbType, $doctrineType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + if (!Types\Type::hasType($doctrineType)) { + throw DBALException::typeNotFound($doctrineType); + } + + $dbType = strtolower($dbType); + $this->doctrineTypeMapping[$dbType] = $doctrineType; + } + + /** + * Gets the Doctrine type that is mapped for the given database column type. + * + * @param string $dbType + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException + */ + public function getDoctrineTypeMapping($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + + if (!isset($this->doctrineTypeMapping[$dbType])) { + throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it."); + } + + return $this->doctrineTypeMapping[$dbType]; + } + + /** + * Checks if a database type is currently supported by this platform. + * + * @param string $dbType + * + * @return boolean + */ + public function hasDoctrineTypeMappingFor($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + + return isset($this->doctrineTypeMapping[$dbType]); + } + + /** + * Initializes the Doctrine Type comments instance variable for in_array() checks. + * + * @return void + */ + protected function initializeCommentedDoctrineTypes() + { + $this->doctrineTypeComments = array(); + + foreach (Type::getTypesMap() as $typeName => $className) { + $type = Type::getType($typeName); + + if ($type->requiresSQLCommentHint($this)) { + $this->doctrineTypeComments[] = $typeName; + } + } + } + + /** + * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? + * + * @param \Doctrine\DBAL\Types\Type $doctrineType + * + * @return boolean + */ + public function isCommentedDoctrineType(Type $doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + return in_array($doctrineType->getName(), $this->doctrineTypeComments); + } + + /** + * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements. + * + * @param string|\Doctrine\DBAL\Types\Type $doctrineType + * + * @return void + */ + public function markDoctrineTypeCommented($doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; + } + + /** + * Gets the comment to append to a column comment that helps parsing this type in reverse engineering. + * + * @param \Doctrine\DBAL\Types\Type $doctrineType + * + * @return string + */ + public function getDoctrineTypeComment(Type $doctrineType) + { + return '(DC2Type:' . $doctrineType->getName() . ')'; + } + + /** + * Gets the comment of a passed column modified by potential doctrine type comment hints. + * + * @param \Doctrine\DBAL\Schema\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 the maximum length of a binary field. + * + * @return integer + */ + public function getBinaryMaxLength() + { + return 4000; + } + + /** + * Gets the default length of a binary field. + * + * @return integer + */ + public function getBinaryDefaultLength() + { + return 255; + } + + /** + * Gets all SQL wildcard characters of the platform. + * + * @return array + */ + public function getWildcards() + { + return array('%', '_'); + } + + /** + * Returns the regular expression operator. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getRegexpExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the global unique identifier expression. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getGuidExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to get 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 SQL snippet to get 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 SQL snippet to get 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 SQL snippet to get the lowest value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a MIN aggregate function. + */ + public function getMinExpression($column) + { + return 'MIN(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the total sum of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a SUM aggregate function. + */ + public function getSumExpression($column) + { + return 'SUM(' . $column . ')'; + } + + // scalar functions + + /** + * Returns the SQL snippet to get the md5 sum of a field. + * + * Note: Not SQL92, but common functionality. + * + * @param string $column + * + * @return string + */ + public function getMd5Expression($column) + { + return 'MD5(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the length of a text field. + * + * @param string $column + * + * @return string + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the squared value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including an SQRT aggregate function. + */ + public function getSqrtExpression($column) + { + return 'SQRT(' . $column . ')'; + } + + /** + * Returns the SQL snippet to round a numeric field to the number of decimals specified. + * + * @param string $column + * @param integer $decimals + * + * @return string + */ + public function getRoundExpression($column, $decimals = 0) + { + return 'ROUND(' . $column . ', ' . $decimals . ')'; + } + + /** + * Returns the SQL snippet to get 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 . ')'; + } + + /** + * Returns the SQL snippet to trim a string. + * + * @param string $str The expression to apply the trim to. + * @param integer $pos The position of the trim (leading/trailing/both). + * @param string|boolean $char The char to trim, has to be quoted already. Defaults to space. + * + * @return string + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $expression = ''; + + switch ($pos) { + case self::TRIM_LEADING: + $expression = 'LEADING '; + break; + + case self::TRIM_TRAILING: + $expression = 'TRAILING '; + break; + + case self::TRIM_BOTH: + $expression = 'BOTH '; + break; + } + + if (false !== $char) { + $expression .= $char . ' '; + } + + if ($pos || false !== $char) { + $expression .= 'FROM '; + } + + return 'TRIM(' . $expression . $str . ')'; + } + + /** + * Returns the SQL snippet to trim trailing space characters from the expression. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getRtrimExpression($str) + { + return 'RTRIM(' . $str . ')'; + } + + /** + * Returns the SQL snippet to trim leading space characters from the expression. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getLtrimExpression($str) + { + return 'LTRIM(' . $str . ')'; + } + + /** + * Returns the SQL snippet to change all characters from the expression 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 . ')'; + } + + /** + * Returns the SQL snippet to change all characters from the expression 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 SQL snippet to get the position of the first occurrence of substring $substr in string $str. + * + * @param string $str Literal string. + * @param string $substr Literal string to find. + * @param integer|boolean $startPos Position to start at, beginning of string by default. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to get the current system date. + * + * @return string + */ + public function getNowExpression() + { + return 'NOW()'; + } + + /** + * Returns a SQL snippet 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|null $length The substring portion length. + * + * @return string + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * Returns a SQL snippet to concatenate the given expressions. + * + * Accepts an arbitrary number of string parameters. Each parameter must contain an expression. + * + * @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')); + * + * + * @param string $expression + * + * @return string The logical expression. + */ + public function getNotExpression($expression) + { + return 'NOT(' . $expression . ')'; + } + + /** + * Returns the SQL that checks if an expression is null. + * + * @param string $expression The expression that should be compared to null. + * + * @return string The logical expression. + */ + public function getIsNullExpression($expression) + { + return $expression . ' IS NULL'; + } + + /** + * Returns the SQL that checks if an expression is not null. + * + * @param string $expression The expression that should be compared to null. + * + * @return string The logical expression. + */ + public function getIsNotNullExpression($expression) + { + return $expression . ' IS NOT NULL'; + } + + /** + * Returns the 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 The logical expression. + */ + public function getBetweenExpression($expression, $value1, $value2) + { + return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; + } + + /** + * Returns the SQL to get the arccosine of a value. + * + * @param string $value + * + * @return string + */ + public function getAcosExpression($value) + { + return 'ACOS(' . $value . ')'; + } + + /** + * Returns the SQL to get the sine of a value. + * + * @param string $value + * + * @return string + */ + public function getSinExpression($value) + { + return 'SIN(' . $value . ')'; + } + + /** + * Returns the SQL to get the PI value. + * + * @return string + */ + public function getPiExpression() + { + return 'PI()'; + } + + /** + * Returns the SQL to get the cosine of a value. + * + * @param string $value + * + * @return string + */ + public function getCosExpression($value) + { + return 'COS(' . $value . ')'; + } + + /** + * Returns the SQL to calculate the difference in days between the two passed dates. + * + * Computes diff = date1 - date2. + * + * @param string $date1 + * @param string $date2 + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateDiffExpression($date1, $date2) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to add the number of given seconds to a date. + * + * @param string $date + * @param integer $seconds + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddSecondsExpression($date, $seconds) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, self::DATE_INTERVAL_UNIT_SECOND); + } + + /** + * Returns the SQL to subtract the number of given seconds from a date. + * + * @param string $date + * @param integer $seconds + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubSecondsExpression($date, $seconds) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, self::DATE_INTERVAL_UNIT_SECOND); + } + + /** + * Returns the SQL to add the number of given minutes to a date. + * + * @param string $date + * @param integer $minutes + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddMinutesExpression($date, $minutes) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, self::DATE_INTERVAL_UNIT_MINUTE); + } + + /** + * Returns the SQL to subtract the number of given minutes from a date. + * + * @param string $date + * @param integer $minutes + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubMinutesExpression($date, $minutes) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, self::DATE_INTERVAL_UNIT_MINUTE); + } + + /** + * Returns the SQL to add the number of given hours to a date. + * + * @param string $date + * @param integer $hours + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddHourExpression($date, $hours) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $hours, self::DATE_INTERVAL_UNIT_HOUR); + } + + /** + * Returns the SQL to subtract the number of given hours to a date. + * + * @param string $date + * @param integer $hours + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubHourExpression($date, $hours) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $hours, self::DATE_INTERVAL_UNIT_HOUR); + } + + /** + * Returns the SQL to add the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddDaysExpression($date, $days) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $days, self::DATE_INTERVAL_UNIT_DAY); + } + + /** + * Returns the SQL to subtract the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubDaysExpression($date, $days) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $days, self::DATE_INTERVAL_UNIT_DAY); + } + + /** + * Returns the SQL to add the number of given weeks to a date. + * + * @param string $date + * @param integer $weeks + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddWeeksExpression($date, $weeks) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, self::DATE_INTERVAL_UNIT_WEEK); + } + + /** + * Returns the SQL to subtract the number of given weeks from a date. + * + * @param string $date + * @param integer $weeks + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubWeeksExpression($date, $weeks) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, self::DATE_INTERVAL_UNIT_WEEK); + } + + /** + * Returns the SQL to add the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddMonthExpression($date, $months) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $months, self::DATE_INTERVAL_UNIT_MONTH); + } + + /** + * Returns the SQL to subtract the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubMonthExpression($date, $months) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $months, self::DATE_INTERVAL_UNIT_MONTH); + } + + /** + * Returns the SQL to add the number of given quarters to a date. + * + * @param string $date + * @param integer $quarters + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddQuartersExpression($date, $quarters) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, self::DATE_INTERVAL_UNIT_QUARTER); + } + + /** + * Returns the SQL to subtract the number of given quarters from a date. + * + * @param string $date + * @param integer $quarters + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubQuartersExpression($date, $quarters) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, self::DATE_INTERVAL_UNIT_QUARTER); + } + + /** + * Returns the SQL to add the number of given years to a date. + * + * @param string $date + * @param integer $years + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddYearsExpression($date, $years) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $years, self::DATE_INTERVAL_UNIT_YEAR); + } + + /** + * Returns the SQL to subtract the number of given years from a date. + * + * @param string $date + * @param integer $years + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubYearsExpression($date, $years) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $years, self::DATE_INTERVAL_UNIT_YEAR); + } + + /** + * Returns the SQL for a date arithmetic expression. + * + * @param string $date The column or literal representing a date to perform the arithmetic operation on. + * @param string $operator The arithmetic operator (+ or -). + * @param integer $interval The interval that shall be calculated into the date. + * @param string $unit The unit of the interval that shall be calculated into the date. + * One of the DATE_INTERVAL_UNIT_* constants. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL bit AND comparison expression. + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' & ' . $value2 . ')'; + } + + /** + * Returns the SQL bit OR comparison expression. + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' | ' . $value2 . ')'; + } + + /** + * Returns the FOR UPDATE expression. + * + * @return string + */ + 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 The FROM clause to append the hint for the given lock mode to. + * @param integer|null $lockMode One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will + * be appended to the FROM clause. + * + * @return string + */ + public function appendLockHint($fromClause, $lockMode) + { + return $fromClause; + } + + /** + * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock. + * + * This defaults to the ANSI 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(); + } + + /** + * Returns 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 ANSI SQL standard. + * + * @return string + */ + public function getWriteLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Returns the SQL snippet to drop an existing database. + * + * @param string $database The name of the database that should be dropped. + * + * @return string + */ + public function getDropDatabaseSQL($database) + { + return 'DROP DATABASE ' . $database; + } + + /** + * Returns the SQL snippet to drop an existing table. + * + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDropTableSQL($table) + { + $tableArg = $table; + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!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; + } + + /** + * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. + * + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropTemporaryTableSQL($table) + { + return $this->getDropTableSQL($table); + } + + /** + * Returns the SQL to drop an index from a table. + * + * @param \Doctrine\DBAL\Schema\Index|string $index + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } elseif (!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + return 'DROP INDEX ' . $index; + } + + /** + * Returns the SQL to drop a constraint. + * + * @param \Doctrine\DBAL\Schema\Constraint|string $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropConstraintSQL($constraint, $table) + { + if ($constraint instanceof Constraint) { + $constraint = $constraint->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; + } + + /** + * Returns the SQL to drop a foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; + } + + /** + * Returns the SQL statement(s) to create a table with the specified name, columns and constraints + * on this platform. + * + * @param \Doctrine\DBAL\Schema\Table $table + * @param integer $createFlags + * + * @return array The sequence of SQL statements. + * + * @throws \Doctrine\DBAL\DBALException + * @throws \InvalidArgumentException + */ + 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->getQuotedColumns($this); + $options['primary_index'] = $index; + } else { + $options['indexes'][$index->getQuotedName($this)] = $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 = $column->toArray(); + $columnData['name'] = $column->getQuotedName($this); + $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false; + $columnData['comment'] = $this->getColumnComment($column); + + if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) { + $columnData['length'] = 255; + } + + 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) { + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment); + } + } + } + + return array_merge($sql, $columnSql); + } + + /** + * @param string $tableName + * @param string $columnName + * @param string $comment + * + * @return string + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) . + " IS " . $comment; + } + + /** + * Returns the SQL used to create a table. + * + * @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; + } + + /** + * @return string + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TEMPORARY TABLE"; + } + + /** + * Returns the SQL to create a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to change a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to create a constraint on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); + + $columnList = '('. implode(', ', $constraint->getQuotedColumns($this)) . ')'; + + $referencesClause = ''; + if ($constraint instanceof 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().' + ); + } + } elseif ($constraint instanceof ForeignKeyConstraint) { + $query .= ' FOREIGN KEY'; + + $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) . + ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')'; + } + $query .= ' '.$columnList.$referencesClause; + + return $query; + } + + /** + * Returns the SQL to create an index on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getCreateIndexSQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + $name = $index->getQuotedName($this); + $columns = $index->getQuotedColumns($this); + + if (count($columns) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + if ($index->isPrimary()) { + return $this->getCreatePrimaryKeySQL($index, $table); + } + + $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')' . $this->getPartialIndexSQL($index); + + return $query; + } + + /** + * Adds condition for partial index. + * + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + protected function getPartialIndexSQL(Index $index) + { + if ($this->supportsPartialIndexes() && $index->hasOption('where')) { + return ' WHERE ' . $index->getOption('where'); + } + + return ''; + } + + /** + * Adds additional flags for index generation. + * + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + protected function getCreateIndexSQLFlags(Index $index) + { + return $index->isUnique() ? 'UNIQUE ' : ''; + } + + /** + * Returns the SQL to create an unnamed primary key constraint. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')'; + } + + /** + * Returns the SQL to create a named schema. + * + * @param string $schemaName + * + * @return string + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateSchemaSQL($schemaName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * 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 separated 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 The identifier name to be quoted. + * + * @return string The quoted identifier string. + */ + public function quoteIdentifier($str) + { + if (strpos($str, ".") !== false) { + $parts = array_map(array($this, "quoteSingleIdentifier"), explode(".", $str)); + + return implode(".", $parts); + } + + return $this->quoteSingleIdentifier($str); + } + + /** + * Quotes a single identifier (no dot chain separation). + * + * @param string $str The identifier name to be quoted. + * + * @return string The quoted identifier string. + */ + public function quoteSingleIdentifier($str) + { + $c = $this->getIdentifierQuoteCharacter(); + + return $c . str_replace($c, $c.$c, $str) . $c; + } + + /** + * Returns the SQL to create a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key constraint. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return string + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); + + return $query; + } + + /** + * Gets the SQL statements for altering an existing table. + * + * This method returns an array of SQL statements, since some platforms need several statements. + * + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getAlterTableSQL(TableDiff $diff) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + 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 \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + 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 \Doctrine\DBAL\Schema\ColumnDiff $columnDiff + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + 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 \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + 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 \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $sql + * + * @return boolean + */ + 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(); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = $diff->getName($this)->getQuotedName($this); + + $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; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = (false !== $diff->newName) + ? $diff->getNewName()->getQuotedName($this) + : $diff->getName($this)->getQuotedName($this); + + $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); + } + + foreach ($diff->renamedIndexes as $oldIndexName => $index) { + $oldIndexName = new Identifier($oldIndexName); + $sql = array_merge( + $sql, + $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName) + ); + } + + return $sql; + } + + /** + * Returns the SQL for renaming an index on a table. + * + * @param string $oldIndexName The name of the index to rename from. + * @param \Doctrine\DBAL\Schema\Index $index The definition of the index to rename to. + * @param string $tableName The table to rename the given index on. + * + * @return array The sequence of SQL statements for renaming the given index. + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + $this->getDropIndexSQL($oldIndexName, $tableName), + $this->getCreateIndexSQL($index, $tableName) + ); + } + + /** + * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. + * + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + /** + * Gets 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) { + $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field); + } + + return implode(', ', $queryFields); + } + + /** + * Obtains DBMS specific SQL code portion needed to declare a generic type + * field to be used in statements like CREATE TABLE. + * + * @param string $name The name the field to be declared. + * @param array $field An 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 " . $this->quoteStringLiteral($field['comment']); + } + + return $name . ' ' . $columnDef; + } + + /** + * Returns 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'] . ')'; + } + + /** + * Obtains DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. + * + * @param array $field The 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", "BigInt", "SmallInt"))) { + $default = " DEFAULT ".$field['default']; + } elseif (in_array((string) $field['type'], array('DateTime', 'DateTimeTz')) && $field['default'] == $this->getCurrentTimestampSQL()) { + $default = " DEFAULT ".$this->getCurrentTimestampSQL(); + } elseif ((string) $field['type'] == 'Time' && $field['default'] == $this->getCurrentTimeSQL()) { + $default = " DEFAULT ".$this->getCurrentTimeSQL(); + } elseif ((string) $field['type'] == 'Date' && $field['default'] == $this->getCurrentDateSQL()) { + $default = " DEFAULT ".$this->getCurrentDateSQL(); + } elseif ((string) $field['type'] == 'Boolean') { + $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + } + } + + return $default; + } + + /** + * Obtains DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. + * + * @param array $definition The 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); + } + + /** + * Obtains DBMS specific SQL code portion needed to set a unique + * constraint declaration to be used in statements like CREATE TABLE. + * + * @param string $name The name of the unique constraint. + * @param \Doctrine\DBAL\Schema\Index $index The index definition. + * + * @return string DBMS specific SQL code portion needed to set a constraint. + * + * @throws \InvalidArgumentException + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $columns = $index->getQuotedColumns($this); + $name = new Identifier($name); + + if (count($columns) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE (' + . $this->getIndexFieldDeclarationListSQL($columns) + . ')' . $this->getPartialIndexSQL($index); + } + + /** + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name The name of the index. + * @param \Doctrine\DBAL\Schema\Index $index The index definition. + * + * @return string DBMS specific SQL code portion needed to set an index. + * + * @throws \InvalidArgumentException + */ + public function getIndexDeclarationSQL($name, Index $index) + { + $columns = $index->getQuotedColumns($this); + $name = new Identifier($name); + + if (count($columns) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' (' + . $this->getIndexFieldDeclarationListSQL($columns) + . ')' . $this->getPartialIndexSQL($index); + } + + /** + * Obtains 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. + * + * @param array $columnDef + * + * @return string + */ + public function getCustomTypeDeclarationSQL(array $columnDef) + { + return $columnDef['columnDefinition']; + } + + /** + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param array $fields + * + * @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); + } + + /** + * Returns 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; + } + + /** + * 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 \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @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; + } + + /** + * Returns the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The 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 the given referential action in uppercase if valid, otherwise throws an exception. + * + * @param string $action The foreign key referential action. + * + * @return string + * + * @throws \InvalidArgumentException if unknown referential action given + */ + public function getForeignKeyReferentialActionSQL($action) + { + $upper = strtoupper($action); + switch ($upper) { + case 'CASCADE': + case 'SET NULL': + case 'NO ACTION': + case 'RESTRICT': + case 'SET DEFAULT': + return $upper; + default: + throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper); + } + } + + /** + * Obtains 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 \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return string + * + * @throws \InvalidArgumentException + */ + 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->getQuotedLocalColumns($this)) + . ') REFERENCES ' + . $foreignKey->getQuotedForeignTableName($this) . ' (' + . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')'; + + return $sql; + } + + /** + * Obtains 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'; + } + + /** + * Obtains 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 The 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 ''; + } + + /** + * Obtains 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 The 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 $this->supportsColumnCollation() ? 'COLLATE ' . $collation : ''; + } + + /** + * 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). + * + * Note: if the input is not a boolean the original input might be returned. + * + * There are two contexts when converting booleans: Literals and Prepared Statements. + * This method should handle the literal case + * + * @param mixed $item A boolean or an array of them. + * + * @return mixed A boolean database value or an array of them. + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $k => $value) { + if (is_bool($value)) { + $item[$k] = (int) $value; + } + } + } elseif (is_bool($item)) { + $item = (int) $item; + } + + return $item; + } + + /** + * Some platforms have boolean literals that needs to be correctly converted + * + * The default conversion tries to convert value into bool "(bool)$item" + * + * @param mixed $item + * + * @return bool|null + */ + public function convertFromBoolean($item) + { + return null === $item ? null: (bool) $item ; + } + + /** + * This method should handle the prepared statements case. When there is no + * distinction, it's OK to use the same method. + * + * Note: if the input is not a boolean the original input might be returned. + * + * @param mixed $item A boolean or an array of them. + * + * @return mixed A boolean database value or an array of them. + */ + public function convertBooleansToDatabaseValue($item) + { + return $this->convertBooleans($item); + } + + /** + * Returns the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'CURRENT_DATE'; + } + + /** + * Returns the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'CURRENT_TIME'; + } + + /** + * Returns the SQL specific for the platform to get the current timestamp + * + * @return string + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT_TIMESTAMP'; + } + + /** + * Returns the SQL for a given transaction isolation level Connection constant. + * + * @param integer $level + * + * @return string + * + * @throws \InvalidArgumentException + */ + 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); + } + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL statement for retrieving the namespaces defined in the database. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListNamespacesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * @param string|null $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableColumnsSQL($table, $database = null) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTablesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to list all views of a database or user. + * + * @param string $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListViewsSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns 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 + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableForeignKeysSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $name + * @param string $sql + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateViewSQL($name, $sql) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $name + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDropViewSQL($name) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to drop an existing sequence. + * + * @param Sequence|string $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $sequenceName + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to create a new database. + * + * @param string $database The name of the database that should be created. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateDatabaseSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to set the transaction isolation level. + * + * @param integer $level + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getSetTransactionIsolationSQL($level) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + + /** + * Obtains DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param array $fieldDeclaration + * + * @return string + */ + 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 receive an auto-generated value from the + * database on insert of a row. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return false; + } + + /** + * Whether the platform emulates identity columns through sequences. + * + * Some platforms that do not support identity columns natively + * but support sequences can emulate identity columns by using + * sequences. + * + * @return boolean + */ + public function usesSequenceEmulatedIdentityColumns() + { + return false; + } + + /** + * Returns the name of the sequence for a particular identity column in a particular table. + * + * @param string $tableName The name of the table to return the sequence name for. + * @param string $columnName The name of the identity column in the table to return the sequence name for. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + * + * @see usesSequenceEmulatedIdentityColumns + */ + public function getIdentitySequenceName($tableName, $columnName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Whether the platform supports indexes. + * + * @return boolean + */ + public function supportsIndexes() + { + return true; + } + + /** + * Whether the platform supports partial indexes. + * + * @return boolean + */ + public function supportsPartialIndexes() + { + return false; + } + + /** + * Whether the platform supports altering tables. + * + * @return boolean + */ + 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; + } + + /** + * Whether the platform supports foreign key constraints. + * + * @return boolean + */ + public function supportsForeignKeyConstraints() + { + return true; + } + + /** + * Whether this platform supports onUpdate in foreign key constraints. + * + * @return boolean + */ + public function supportsForeignKeyOnUpdate() + { + return ($this->supportsForeignKeyConstraints() && true); + } + + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return false; + } + + /** + * Whether this platform can emulate schemas. + * + * Platforms that either support or emulate schemas don't automatically + * filter a schema for the namespaced elements in {@link + * AbstractManager#createSchema}. + * + * @return boolean + */ + public function canEmulateSchemas() + { + return false; + } + + /** + * Returns the default schema name. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDefaultSchemaName() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Whether this platform supports create database. + * + * Some databases don't allow to create and drop databases at all or only with certain tools. + * + * @return boolean + */ + 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; + } + + /** + * Whether this platform support to add inline column comments as postfix. + * + * @return boolean + */ + public function supportsInlineColumnComments() + { + return false; + } + + /** + * Whether this platform support the proprietary syntax "COMMENT ON asset". + * + * @return boolean + */ + public function supportsCommentOnStatement() + { + return false; + } + + /** + * Does this platform have native guid type. + * + * @return boolean + */ + public function hasNativeGuidType() + { + return false; + } + + /** + * Does this platform have native JSON type. + * + * @return boolean + */ + public function hasNativeJsonType() + { + return false; + } + + /** + * @deprecated + * @todo Remove in 3.0 + */ + public function getIdentityColumnNullInsertSQL() + { + return ""; + } + + /** + * Whether this platform supports views. + * + * @return boolean + */ + public function supportsViews() + { + return true; + } + + /** + * Does this platform support column collation? + * + * @return boolean + */ + public function supportsColumnCollation() + { + return false; + } + + /** + * 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'; + } + + /** + * Adds an driver-specific LIMIT clause to the query. + * + * @param string $query + * @param integer|null $limit + * @param integer|null $offset + * + * @return string + * + * @throws DBALException + */ + 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); + } + + /** + * Adds an driver-specific LIMIT clause to the query. + * + * @param string $query + * @param integer|null $limit + * @param integer|null $offset + * + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + } + + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + + return $query; + } + + /** + * Whether the database platform support offsets in modify limit clauses. + * + * @return boolean + */ + 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 $schemaElementName + * + * @return string + */ + public function fixSchemaElementName($schemaElementName) + { + return $schemaElementName; + } + + /** + * Maximum length of any given database identifier, like tables or column names. + * + * @return integer + */ + public function getMaxIdentifierLength() + { + return 63; + } + + /** + * Returns the insert SQL for an empty insert statement. + * + * @param string $tableName + * @param string $identifierColumnName + * + * @return string + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)'; + } + + /** + * Generates 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 boolean $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'; + } + + /** + * Returns the SQL to create a new savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function createSavePoint($savepoint) + { + return 'SAVEPOINT ' . $savepoint; + } + + /** + * Returns the SQL to release a savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function releaseSavePoint($savepoint) + { + return 'RELEASE SAVEPOINT ' . $savepoint; + } + + /** + * Returns the SQL to rollback a savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TO SAVEPOINT ' . $savepoint; + } + + /** + * Returns the keyword list instance of this platform. + * + * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList + * + * @throws \Doctrine\DBAL\DBALException If no keyword list is specified. + */ + final public function getReservedKeywordsList() + { + // Check for an existing instantiation of the keywords class. + if ($this->_keywords) { + return $this->_keywords; + } + + $class = $this->getReservedKeywordsClass(); + $keywords = new $class; + if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) { + throw DBALException::notSupported(__METHOD__); + } + + // Store the instance so it doesn't need to be generated on every request. + $this->_keywords = $keywords; + + return $keywords; + } + + /** + * Returns the class name of the reserved keywords list. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getReservedKeywordsClass() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Quotes a literal string. + * This method is NOT meant to fix SQL injections! + * It is only meant to escape this platform's string literal + * quote character inside the given literal string. + * + * @param string $str The literal string to be quoted. + * + * @return string The quoted literal string. + */ + public function quoteStringLiteral($str) + { + $c = $this->getStringLiteralQuoteCharacter(); + + return $c . str_replace($c, $c . $c, $str) . $c; + } + + /** + * Gets the character used for string literal quoting. + * + * @return string + */ + public function getStringLiteralQuoteCharacter() + { + return "'"; + } +} 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 00000000000..9cc794d16a1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -0,0 +1,842 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; + +class DB2Platform extends AbstractPlatform +{ + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 32704; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 1; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + // todo blob(n) with $field['length']; + return 'BLOB(1M)'; + } + + /** + * {@inheritDoc} + */ + public function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'bigint' => 'bigint', + 'integer' => 'integer', + 'time' => 'time', + 'date' => 'date', + 'varchar' => 'string', + 'character' => 'string', + 'varbinary' => 'binary', + 'binary' => 'binary', + 'clob' => 'text', + 'blob' => 'blob', + 'decimal' => 'decimal', + 'double' => 'float', + 'real' => 'float', + 'timestamp' => 'datetime', + ); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + // todo clob(n) with $field['length']; + return 'CLOB(1M)'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'db2'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; + } + + return $autoinc; + } + + /** + * {@inheritdoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND(' . $value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return 'BITOR(' . $value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_WEEK: + $interval *= 7; + $unit = self::DATE_INTERVAL_UNIT_DAY; + break; + + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + break; + } + + return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit; + } + + /** + * {@inheritdoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return "TIMESTAMP(0) WITH DEFAULT"; + } + + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritdoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE ' . $tableName . ' IMMEDIATE'; + } + + /** + * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited. + * + * @license New BSD License + * + * @param string $table + * @param string $database + * + * @return string + */ + public function getListTableColumnsSQL($table, $database = null) + { + // We do the funky subquery and join syscat.columns.default this crazy way because + // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB, + // it wants shorter stuff like a varchar. + return " + SELECT + cols.default, + subq.* + FROM ( + SELECT DISTINCT + c.tabschema, + c.tabname, + c.colname, + c.colno, + c.typename, + c.nulls, + c.length, + c.scale, + c.identity, + tc.type AS tabconsttype, + k.colseq, + CASE + WHEN c.generated = 'D' THEN 1 + ELSE 0 + END AS autoincrement + 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 + ) subq + JOIN syscat.columns cols + ON subq.tabschema = cols.tabschema + AND subq.tabname = cols.tabname + AND subq.colno = cols.colno + ORDER BY subq.colno + "; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT idx.INDNAME AS key_name, + idxcol.COLNAME AS column_name, + CASE + WHEN idx.UNIQUERULE = 'P' THEN 1 + ELSE 0 + END AS primary, + CASE + WHEN idx.UNIQUERULE = 'D' THEN 1 + ELSE 0 + END AS non_unique + FROM SYSCAT.INDEXES AS idx + JOIN SYSCAT.INDEXCOLUSE AS idxcol + ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME + WHERE idx.TABNAME = UPPER('" . $table . "') + ORDER BY idxcol.COLSEQ ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table) + { + return "SELECT fkcol.COLNAME AS local_column, + fk.REFTABNAME AS foreign_table, + pkcol.COLNAME AS foreign_column, + fk.CONSTNAME AS index_name, + CASE + WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_update, + CASE + WHEN fk.DELETERULE = 'C' THEN 'CASCADE' + WHEN fk.DELETERULE = 'N' THEN 'SET NULL' + WHEN fk.DELETERULE = 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_delete + FROM SYSCAT.REFERENCES AS fk + JOIN SYSCAT.KEYCOLUSE AS fkcol + ON fk.CONSTNAME = fkcol.CONSTNAME + AND fk.TABSCHEMA = fkcol.TABSCHEMA + AND fk.TABNAME = fkcol.TABNAME + JOIN SYSCAT.KEYCOLUSE AS pkcol + ON fk.REFKEYNAME = pkcol.CONSTNAME + AND fk.REFTABSCHEMA = pkcol.TABSCHEMA + AND fk.REFTABNAME = pkcol.TABNAME + WHERE fk.TABNAME = UPPER('" . $table . "') + ORDER BY fkcol.COLSEQ ASC"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW ".$name." AS ".$sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return "DROP VIEW ".$name; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($database) + { + return "CREATE DATABASE ".$database; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return "DROP DATABASE " . $database; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDateSQL() + { + return 'CURRENT DATE'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimeSQL() + { + return 'CURRENT TIME'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimestampSQL() + { + return "CURRENT TIMESTAMP"; + } + + /** + * {@inheritDoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + // Index declaration in statements like CREATE TABLE is not supported. + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + 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 $definition) { + $sqls[] = $this->getCreateIndexSQL($definition, $tableName); + } + return $sqls; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + $commentsSQL = array(); + + $queryParts = array(); + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + // Adding non-nullable columns to a table requires a default value to be specified. + if ( ! empty($columnDef['notnull']) && + ! isset($columnDef['default']) && + empty($columnDef['autoincrement']) + ) { + $queryPart .= ' WITH DEFAULT'; + } + + $queryParts[] = $queryPart; + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + 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; + } + + if ($columnDiff->hasChanged('comment')) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $columnDiff->column->getQuotedName($this), + $this->getColumnComment($columnDiff->column) + ); + + if (count($columnDiff->changedProperties) === 1) { + continue; + } + } + + $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . + ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts); + } + + // Some table alteration operations require a table reorganization. + if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) { + $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')"; + } + + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Gathers the table alteration SQL for a given column diff. + * + * @param Table $table The table to gather the SQL for. + * @param ColumnDiff $columnDiff The column diff to evaluate. + * @param array $sql The sequence of table alteration statements to fill. + * @param array $queryParts The sequence of column alteration clauses to fill. + */ + private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts) + { + $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff); + + if (empty($alterColumnClauses)) { + return; + } + + // If we have a single column alteration, we can append the clause to the main query. + if (count($alterColumnClauses) === 1) { + $queryParts[] = current($alterColumnClauses); + + return; + } + + // We have multiple alterations for the same column, + // so we need to trigger a complete ALTER TABLE statement + // for each ALTER COLUMN clause. + foreach ($alterColumnClauses as $alterColumnClause) { + $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause; + } + } + + /** + * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff. + * + * @param ColumnDiff $columnDiff The column diff to evaluate. + * + * @return array + */ + private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) + { + $column = $columnDiff->column->toArray(); + + $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this); + + if ($column['columnDefinition']) { + return array($alterClause . ' ' . $column['columnDefinition']); + } + + $clauses = array(); + + if ($columnDiff->hasChanged('type') || + $columnDiff->hasChanged('length') || + $columnDiff->hasChanged('precision') || + $columnDiff->hasChanged('scale') || + $columnDiff->hasChanged('fixed') + ) { + $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this); + } + + if ($columnDiff->hasChanged('notnull')) { + $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL'; + } + + if ($columnDiff->hasChanged('default')) { + if (isset($column['default'])) { + $defaultClause = $this->getDefaultValueDeclarationSQL($column); + + if ($defaultClause) { + $clauses[] = $alterClause . ' SET' . $defaultClause; + } + } else { + $clauses[] = $alterClause . ' DROP DEFAULT'; + } + } + + return $clauses; + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + if ($remIndex->isPrimary()) { + $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } elseif ($remIndex->isUnique()) { + $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this); + } else { + $sql[] = $this->getDropIndexSQL($remIndex, $table); + } + + $sql[] = $this->getCreateIndexSQL($addIndex, $table); + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! empty($field['autoincrement'])) { + return ''; + } + + if (isset($field['version']) && $field['version']) { + if ((string) $field['type'] != "DateTime") { + $field['default'] = "1"; + } + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "DECLARE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return "SESSION." . $tableName; + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTR(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * DB2 returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' WITH RR USE AND KEEP UPDATE LOCKS'; + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM sysibm.sysdummy1'; + } + + /** + * {@inheritDoc} + * + * 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. + */ + public function supportsSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php new file mode 100644 index 00000000000..08f053b08c6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php @@ -0,0 +1,624 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\BinaryType; + +/** + * Drizzle platform + * + * @author Kim Hemsø Rasmussen + */ +class DrizzlePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getName() + { + return 'drizzle'; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = '+' === $operator ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + + return $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'; + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'varchar' => 'string', + 'varbinary' => 'binary', + 'integer' => 'integer', + 'blob' => 'blob', + 'decimal' => 'decimal', + 'datetime' => 'datetime', + 'date' => 'date', + 'time' => 'time', + 'text' => 'text', + 'timestamp' => 'datetime', + 'double' => 'float', + 'bigint' => 'bigint', + ); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + 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 . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Build SQL for table options + * + * @param array $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = array(); + + // Collate + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $tableOptions[] = sprintf('COLLATE %s', $options['collate']); + + // Engine + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableOptions[] = sprintf("COMMENT = %s ", $this->quoteStringLiteral($comment)); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param array $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return (isset($options['partition_options'])) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME='LOCAL'"; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\DrizzleKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, IS_AUTO_INCREMENT, CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT," . + " NUMERIC_PRECISION, NUMERIC_SCALE, COLLATION_NAME" . + " FROM DATA_DICTIONARY.COLUMNS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME = '" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT CONSTRAINT_NAME, CONSTRAINT_COLUMNS, REFERENCED_TABLE_NAME, REFERENCED_TABLE_COLUMNS, UPDATE_RULE, DELETE_RULE" . + " FROM DATA_DICTIONARY.FOREIGN_KEYS" . + " WHERE CONSTRAINT_SCHEMA=" . $database . " AND CONSTRAINT_TABLE='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT INDEX_NAME AS 'key_name', COLUMN_NAME AS 'column_name', IS_USED_IN_PRIMARY AS 'primary', IS_UNIQUE=0 AS 'non_unique'" . + " FROM DATA_DICTIONARY.INDEX_PARTS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsViews() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } elseif (is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // drizzle 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; + } + + /** + * {@inheritDoc} + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + foreach ($diff->addedColumns as $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(); + + // Do not generate column alteration clause if type is binary and only fixed property has changed. + // Drizzle only supports binary type columns with variable length. + // Avoids unnecessary table alteration statements. + if ($columnArray['type'] instanceof BinaryType && + $columnDiff->hasChanged('fixed') && + count($columnDiff->changedProperties) === 1 + ) { + continue; + } + + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * {@inheritDoc} + */ + 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'; + } + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 'true' : 'false'; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } +} 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 00000000000..ff8aac81c6e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php @@ -0,0 +1,441 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * DB2 Keywords. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Keywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'DB2'; + } + + /** + * {@inheritdoc} + */ + 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/DrizzleKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php new file mode 100644 index 00000000000..21d59d8c587 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php @@ -0,0 +1,345 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Drizzle Keywordlist. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ABS', + 'ALL', + 'ALLOCATE', + 'ALTER', + 'AND', + 'ANY', + 'ARE', + 'ARRAY', + 'AS', + 'ASENSITIVE', + 'ASYMMETRIC', + 'AT', + 'ATOMIC', + 'AUTHORIZATION', + 'AVG', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOOLEAN', + 'BOTH', + 'BY', + 'CALL', + 'CALLED', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CEIL', + 'CEILING', + 'CHAR', + 'CHARACTER', + 'CHARACTER_LENGTH', + 'CHAR_LENGTH', + 'CHECK', + 'CLOB', + 'CLOSE', + 'COALESCE', + 'COLLATE', + 'COLLECT', + 'COLUMN', + 'COMMIT', + 'CONDITION', + 'CONNECT', + 'CONSTRAINT', + 'CONVERT', + 'CORR', + 'CORRESPONDING', + 'COUNT', + 'COVAR_POP', + 'COVAR_SAMP', + 'CREATE', + 'CROSS', + 'CUBE', + 'CUME_DIST', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_DEFAULT_TRANSFORM_GROUP', + 'CURRENT_PATH', + 'CURRENT_ROLE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TRANSFORM_GROUP_FOR_TYPE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATE', + 'DAY', + 'DEALLOCATE', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENSE_RANK', + 'DEREF', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISCONNECT', + 'DISTINCT', + 'DOUBLE', + 'DROP', + 'DYNAMIC', + 'EACH', + 'ELEMENT', + 'ELSE', + 'END', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXP', + 'EXTERNAL', + 'EXTRACT', + 'FALSE', + 'FETCH', + 'FILTER', + 'FLOAT', + 'FLOOR', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'FUSION', + 'GET', + 'GLOBAL', + 'GRANT', + 'GROUP', + 'GROUPING', + 'HAVING', + 'HOLD', + 'HOUR', + 'IDENTITY', + 'IN', + 'INDICATOR', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INTEGER', + 'INTERSECT', + 'INTERSECTION', + 'INTERVAL', + 'INTO', + 'IS', + 'JOIN', + 'LANGUAGE', + 'LARGE', + 'LATERAL', + 'LEADING', + 'LEFT', + 'LIKE', + 'LN', + 'LOCAL', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOWER', + 'MATCH', + 'MAX', + 'MEMBER', + 'MERGE', + 'METHOD', + 'MIN', + 'MINUTE', + 'MOD', + 'MODIFIES', + 'MODULE', + 'MONTH', + 'MULTISET', + 'NATIONAL', + 'NATURAL', + 'NCHAR', + 'NCLOB', + 'NEW', + 'NO', + 'NONE', + 'NORMALIZE', + 'NOT', + 'NULL_SYM', + 'NULLIF', + 'NUMERIC', + 'OCTET_LENGTH', + 'OF', + 'OLD', + 'ON', + 'ONLY', + 'OPEN', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERLAPS', + 'OVERLAY', + 'PARAMETER', + 'PARTITION', + 'PERCENTILE_CONT', + 'PERCENTILE_DISC', + 'PERCENT_RANK', + 'POSITION', + 'POWER', + 'PRECISION', + 'PREPARE', + 'PRIMARY', + 'PROCEDURE', + 'RANGE', + 'RANK', + 'READS', + 'REAL', + 'RECURSIVE', + 'REF', + 'REFERENCES', + 'REFERENCING', + 'REGR_AVGX', + 'REGR_AVGY', + 'REGR_COUNT', + 'REGR_INTERCEPT', + 'REGR_R2', + 'REGR_SLOPE', + 'REGR_SXX', + 'REGR_SXY', + 'REGR_SYY', + 'RELEASE', + 'RESULT', + 'RETURN', + 'RETURNS', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROLLUP', + 'ROW', + 'ROWS', + 'ROW_NUMBER', + 'SAVEPOINT', + 'SCOPE', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SELECT', + 'SENSITIVE', + 'SESSION_USER', + 'SET', + 'SIMILAR', + 'SMALLINT', + 'SOME', + 'SPECIFIC', + 'SPECIFICTYPE', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQRT', + 'START', + 'STATIC', + 'STDDEV_POP', + 'STDDEV_SAMP', + 'SUBMULTISET', + 'SUBSTRING', + 'SUM', + 'SYMMETRIC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TIMEZONE_HOUR', + 'TIMEZONE_MINUTE', + 'TO', + 'TRAILING', + 'TRANSLATE', + 'TRANSLATION', + 'TREAT', + 'TRIGGER', + 'TRIM', + 'TRUE', + 'UESCAPE', + 'UNION', + 'UNIQUE', + 'UNKNOWN', + 'UNNEST', + 'UPDATE', + 'UPPER', + 'USER', + 'USING', + 'VALUE', + 'VALUES', + 'VARCHAR', + 'VARYING', + 'VAR_POP', + 'VAR_SAMP', + 'WHEN', + 'WHENEVER', + 'WHERE', + 'WIDTH_BUCKET', + 'WINDOW', + 'WITH', + 'WITHIN', + 'WITHOUT', + 'XML', + 'XMLAGG', + 'XMLATTRIBUTES', + 'XMLBINARY', + 'XMLCOMMENT', + 'XMLCONCAT', + 'XMLELEMENT', + 'XMLFOREST', + 'XMLNAMESPACES', + 'XMLPARSE', + 'XMLPI', + 'XMLROOT', + 'XMLSERIALIZE', + 'YEAR', + ); + } +} 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 00000000000..5b8d74d0129 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Abstract interface for a SQL reserved keyword dictionary. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class KeywordList +{ + /** + * @var array|null + */ + private $keywords = null; + + /** + * Checks if the given word is a keyword of this dialect/vendor platform. + * + * @param string $word + * + * @return boolean + */ + public function isKeyword($word) + { + if ($this->keywords === null) { + $this->initializeKeywords(); + } + + return isset($this->keywords[strtoupper($word)]); + } + + /** + * @return void + */ + protected function initializeKeywords() + { + $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); + } + + /** + * Returns the list of keywords. + * + * @return array + */ + abstract protected function getKeywords(); + + /** + * Returns the 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 00000000000..9c8f614993a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php @@ -0,0 +1,43 @@ +. + */ + + +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 + * @author Steve Müller + * @deprecated Use SQLServerKeywords class instead. + */ +class MsSQLKeywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MsSQL'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php new file mode 100644 index 00000000000..7d7b2020877 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php @@ -0,0 +1,281 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL 5.7 reserved keywords list. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class MySQL57Keywords extends MySQLKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MySQL57'; + } + + /** + * {@inheritdoc} + * + * @link http://dev.mysql.com/doc/mysqld-version-reference/en/mysqld-version-reference-reservedwords-5-7.html + */ + protected function getKeywords() + { + return array( + 'ACCESSIBLE', + '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', + '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', + 'GET', + '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', + 'IO_AFTER_GTIDS', + 'IO_BEFORE_GTIDS', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINEAR', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MASTER_BIND', + 'MASTER_SSL_VERIFY_SERVER_CERT', + 'MATCH', + 'MAXVALUE', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NO_WRITE_TO_BINLOG', + 'NONBLOCKING', + 'NOT', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PARTITION', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RANGE', + 'READ', + 'READ_WRITE', + 'READS', + 'REAL', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESIGNAL', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SIGNAL', + 'SMALLINT', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + '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', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + } +} 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 00000000000..ba257741bf6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php @@ -0,0 +1,273 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class MySQLKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MySQL'; + } + + /** + * {@inheritdoc} + */ + 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', + 'RANGE', + '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', + ); + } +} 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 00000000000..b37fdad3deb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Oracle Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class OracleKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'Oracle'; + } + + /** + * {@inheritdoc} + */ + 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', + 'RANGE', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php new file mode 100644 index 00000000000..4e5c632d358 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL 9.1 reserved keywords list. + * + * @author Martin Hasoň + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL91Keywords extends PostgreSQLKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL91'; + } + + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.1/static/sql-keywords-appendix.html + */ + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'ARRAY', + 'AS', + 'ASC', + 'ASYMMETRIC', + 'AUTHORIZATION', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONCURRENTLY', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_CATALOG', + 'CURRENT_DATE', + 'CURRENT_ROLE', + 'CURRENT_SCHEMA', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FETCH', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFFSET', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'RETURNING', + 'RIGHT', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'SYMMETRIC', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VARIADIC', + 'VERBOSE', + 'WHEN', + 'WHERE', + 'WINDOW', + 'WITH', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php new file mode 100644 index 00000000000..7180dfd9834 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL 9.2 reserved keywords list. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL92Keywords extends PostgreSQL91Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL92'; + } + + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.2/static/sql-keywords-appendix.html + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'COLLATION', + )); + } +} 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 00000000000..9e49d7f2504 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Marcelo Santos Araujo + */ +class PostgreSQLKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL'; + } + + /** + * {@inheritdoc} + */ + 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' + ); + } +} 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 00000000000..e1fdd492d8c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php @@ -0,0 +1,143 @@ +. + */ + +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(); + + /** + * @param \Doctrine\DBAL\Platforms\Keywords\KeywordList[] $keywordLists + */ + public function __construct(array $keywordLists) + { + $this->keywordLists = $keywordLists; + } + + /** + * @return array + */ + 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; + } + + /** + * @param string $asset + * @param array $violatedPlatforms + * + * @return void + */ + private function addViolation($asset, $violatedPlatforms) + { + if ( ! $violatedPlatforms) { + return; + } + + $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + $this->addViolation( + 'Table ' . $table->getName() . ' column ' . $column->getName(), + $this->isReservedWord($column->getName()) + ); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->addViolation( + 'Table ' . $table->getName(), + $this->isReservedWord($table->getName()) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php new file mode 100644 index 00000000000..12eacbf9b9b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 11 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere11Keywords extends SQLAnywhereKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere11'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/1100/en/dbreference_en11/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + array_diff( + parent::getKeywords(), + array('IQ') + ), + array( + 'MERGE', + 'OPENSTRING' + ) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php new file mode 100644 index 00000000000..22e04893e98 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 12 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere12Keywords extends SQLAnywhere11Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere12'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/1200/en/dbreference/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + array_diff( + parent::getKeywords(), + array( + 'INDEX_LPAREN', + 'SYNTAX_ERROR', + 'WITH_CUBE', + 'WITH_LPAREN', + 'WITH_ROLLUP' + ) + ), + array( + 'DATETIMEOFFSET', + 'LIMIT', + 'OPENXML', + 'SPATIAL', + 'TREAT' + ) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php new file mode 100644 index 00000000000..4d78b84109f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 16 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere16Keywords extends SQLAnywhere12Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere16'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/index.html#sa160/en/dbreference/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + parent::getKeywords(), + array( + 'ARRAY', + 'JSON', + 'ROW', + 'ROWTYPE', + 'UNNEST', + 'VARRAY' + ) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php new file mode 100644 index 00000000000..cabf1e44914 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php @@ -0,0 +1,281 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 10 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhereKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere'; + } + + /** + * {@inheritdoc} + * + * @link http://infocenter.sybase.com/help/topic/com.sybase.dbrfen10/pdf/dbrfen10.pdf?noframes=true + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'ATTACH', + 'BACKUP', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BIT', + 'BOTTOM', + 'BREAK', + 'BY', + 'CALL', + 'CAPABILITY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHAR', + 'CHAR_CONVERT', + 'CHARACTER', + 'CHECK', + 'CHECKPOINT', + 'CLOSE', + 'COMMENT', + 'COMMIT', + 'COMPRESSED', + 'CONFLICT', + 'CONNECT', + 'CONSTRAINT', + 'CONTAINS', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CUBE', + 'CURRENT', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATE', + 'DBSPACE', + 'DEALLOCATE', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DELETING', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DO', + 'DOUBLE', + 'DROP', + 'DYNAMIC', + 'ELSE', + 'ELSEIF', + 'ENCRYPTED', + 'END', + 'ENDIF', + 'ESCAPE', + 'EXCEPT', + 'EXCEPTION', + 'EXEC', + 'EXECUTE', + 'EXISTING', + 'EXISTS', + 'EXTERNLOGIN', + 'FETCH', + 'FIRST', + 'FLOAT', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FORWARD', + 'FROM', + 'FULL', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HOLDLOCK', + 'IDENTIFIED', + 'IF', + 'IN', + 'INDEX', + 'INDEX_LPAREN', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INSERTING', + 'INSTALL', + 'INSTEAD', + 'INT', + 'INTEGER', + 'INTEGRATED', + 'INTERSECT', + 'INTO', + 'IQ', + 'IS', + 'ISOLATION', + 'JOIN', + 'KERBEROS', + 'KEY', + 'LATERAL', + 'LEFT', + 'LIKE', + 'LOCK', + 'LOGIN', + 'LONG', + 'MATCH', + 'MEMBERSHIP', + 'MESSAGE', + 'MODE', + 'MODIFY', + 'NATURAL', + 'NCHAR', + 'NEW', + 'NO', + 'NOHOLDLOCK', + 'NOT', + 'NOTIFY', + 'NULL', + 'NUMERIC', + 'NVARCHAR', + 'OF', + 'OFF', + 'ON', + 'OPEN', + 'OPTION', + 'OPTIONS', + 'OR', + 'ORDER', + 'OTHERS', + 'OUT', + 'OUTER', + 'OVER', + 'PASSTHROUGH', + 'PRECISION', + 'PREPARE', + 'PRIMARY', + 'PRINT', + 'PRIVILEGES', + 'PROC', + 'PROCEDURE', + 'PUBLICATION', + 'RAISERROR', + 'READTEXT', + 'REAL', + 'REFERENCE', + 'REFERENCES', + 'REFRESH', + 'RELEASE', + 'REMOTE', + 'REMOVE', + 'RENAME', + 'REORGANIZE', + 'RESOURCE', + 'RESTORE', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROLLUP', + 'SAVE', + 'SAVEPOINT', + 'SCROLL', + 'SELECT', + 'SENSITIVE', + 'SESSION', + 'SET', + 'SETUSER', + 'SHARE', + 'SMALLINT', + 'SOME', + 'SQLCODE', + 'SQLSTATE', + 'START', + 'STOP', + 'SUBTRANS', + 'SUBTRANSACTION', + 'SYNCHRONIZE', + 'SYNTAX_ERROR', + 'TABLE', + 'TEMPORARY', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TINYINT', + 'TO', + 'TOP', + 'TRAN', + 'TRIGGER', + 'TRUNCATE', + 'TSEQUAL', + 'UNBOUNDED', + 'UNION', + 'UNIQUE', + 'UNIQUEIDENTIFIER', + 'UNKNOWN', + 'UNSIGNED', + 'UPDATE', + 'UPDATING', + 'USER', + 'USING', + 'VALIDATE', + 'VALUES', + 'VARBINARY', + 'VARBIT', + 'VARCHAR', + 'VARIABLE', + 'VARYING', + 'VIEW', + 'WAIT', + 'WAITFOR', + 'WHEN', + 'WHERE', + 'WHILE', + 'WINDOW', + 'WITH', + 'WITH_CUBE', + 'WITH_LPAREN', + 'WITH_ROLLUP', + 'WITHIN', + 'WORK', + 'WRITETEXT', + 'XML' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php new file mode 100644 index 00000000000..3a7434910f3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2005 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2005Keywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2005'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-US/library/ms189822%28v=sql.90%29.aspx + */ + protected function getKeywords() + { + return array_merge(array_diff(parent::getKeywords(), array('DUMMY')), array( + 'EXTERNAL', + 'PIVOT', + 'REVERT', + 'SECURITYAUDIT', + 'TABLESAMPLE', + 'UNPIVOT' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php new file mode 100644 index 00000000000..38556b50557 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2008 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2008Keywords extends SQLServer2005Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2008'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822%28v=sql.100%29.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'MERGE' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php new file mode 100644 index 00000000000..ada1c026bd7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2012 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2012Keywords extends SQLServer2008Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2012'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'SEMANTICKEYPHRASETABLE', + 'SEMANTICSIMILARITYDETAILSTABLE', + 'SEMANTICSIMILARITYTABLE', + 'TRY_CONVERT', + 'WITHIN GROUP' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php new file mode 100644 index 00000000000..fb43d2d5313 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2000 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + * @author Steve Müller + */ +class SQLServerKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/aa238507%28v=sql.80%29.aspx + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BACKUP', + 'BEGIN', + 'BETWEEN', + 'BREAK', + 'BROWSE', + 'BULK', + 'BY', + 'CASCADE', + 'CASE', + 'CHECK', + 'CHECKPOINT', + 'CLOSE', + 'CLUSTERED', + 'COALESCE', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'COMPUTE', + 'CONSTRAINT', + 'CONTAINS', + 'CONTAINSTABLE', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DBCC', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENY', + 'DESC', + 'DISK', + 'DISTINCT', + 'DISTRIBUTED', + 'DOUBLE', + 'DROP', + 'DUMP', + 'ELSE', + 'END', + 'ERRLVL', + 'ESCAPE', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXTERNAL', + 'FETCH', + 'FILE', + 'FILLFACTOR', + 'FOR', + 'FOREIGN', + 'FREETEXT', + 'FREETEXTTABLE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HOLDLOCK', + 'IDENTITY', + 'IDENTITY_INSERT', + 'IDENTITYCOL', + 'IF', + 'IN', + 'INDEX', + 'INNER', + 'INSERT', + 'INTERSECT', + 'INTO', + 'IS', + 'JOIN', + 'KEY', + 'KILL', + 'LEFT', + 'LIKE', + 'LINENO', + 'LOAD', + 'NATIONAL', + 'NOCHECK ', + 'NONCLUSTERED', + 'NOT', + 'NULL', + 'NULLIF', + 'OF', + 'OFF', + 'OFFSETS', + 'ON', + 'OPEN', + 'OPENDATASOURCE', + 'OPENQUERY', + 'OPENROWSET', + 'OPENXML', + 'OPTION', + 'OR', + 'ORDER', + 'OUTER', + 'OVER', + 'PERCENT', + 'PIVOT', + 'PLAN', + 'PRECISION', + 'PRIMARY', + 'PRINT', + 'PROC', + 'PROCEDURE', + 'PUBLIC', + 'RAISERROR', + 'READ', + 'READTEXT', + 'RECONFIGURE', + 'REFERENCES', + 'REPLICATION', + 'RESTORE', + 'RESTRICT', + 'RETURN', + 'REVERT', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROWCOUNT', + 'ROWGUIDCOL', + 'RULE', + 'SAVE', + 'SCHEMA', + 'SECURITYAUDIT', + 'SELECT', + 'SESSION_USER', + 'SET', + 'SETUSER', + 'SHUTDOWN', + 'SOME', + 'STATISTICS', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'TEXTSIZE', + 'THEN', + 'TO', + 'TOP', + 'TRAN', + 'TRANSACTION', + 'TRIGGER', + 'TRUNCATE', + 'TSEQUAL', + 'UNION', + 'UNIQUE', + 'UNPIVOT', + 'UPDATE', + 'UPDATETEXT', + 'USE', + 'USER', + 'VALUES', + 'VARYING', + 'VIEW', + 'WAITFOR', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITETEXT' + ); + } +} 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 00000000000..a162d3d5396 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php @@ -0,0 +1,168 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SQLite Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLiteKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLite'; + } + + /** + * {@inheritdoc} + */ + 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' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php new file mode 100644 index 00000000000..36b525e2063 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Provides the behavior, features and SQL dialect of the MySQL 5.7 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class MySQL57Platform extends MySqlPlatform +{ + /** + * {@inheritdoc} + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + 'ALTER TABLE ' . $tableName . ' RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this) + ); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords'; + } +} 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 00000000000..e22851b95a2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -0,0 +1,1048 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\BlobType; +use Doctrine\DBAL\Types\TextType; + +/** + * 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 +{ + const LENGTH_LIMIT_TINYTEXT = 255; + const LENGTH_LIMIT_TEXT = 65535; + const LENGTH_LIMIT_MEDIUMTEXT = 16777215; + + const LENGTH_LIMIT_TINYBLOB = 255; + const LENGTH_LIMIT_BLOB = 65535; + const LENGTH_LIMIT_MEDIUMBLOB = 16777215; + + /** + * Adds MySQL-specific LIMIT clause to the query + * 18446744073709551615 is 2^64-1 maximum of unsigned BIGINT the biggest limit possible + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + } elseif ($offset !== null) { + $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = '+' === $operator ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + * + * Two approaches to listing the table indexes. The information_schema is + * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". + */ + 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 . "'"; + } + + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = '".$database."'"; + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * TINYTEXT : 2 ^ 8 - 1 = 255 + * TEXT : 2 ^ 16 - 1 = 65535 + * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 + * LONGTEXT : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getClobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYTEXT) { + return 'TINYTEXT'; + } + + if ($length <= static::LENGTH_LIMIT_TEXT) { + return 'TEXT'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { + return 'MEDIUMTEXT'; + } + } + + return 'LONGTEXT'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + 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. + * + * @deprecated Deprecated since version 2.5, Use {@link self::getColumnCollationDeclarationSQL()} instead. + * + * @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 $this->getColumnCollationDeclarationSQL($collation); + } + + /** + * {@inheritDoc} + * + * MySql prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * MySql supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = '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 Collation ". + "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = " . $database . " AND TABLE_NAME = '" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + 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 . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql[] = $query; + $engine = 'INNODB'; + + if (isset($options['engine'])) { + $engine = strtoupper(trim($options['engine'])); + } + + // Propagate foreign key constraints only for InnoDB. + if (isset($options['foreignKeys']) && $engine === 'INNODB') { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + // Unset the default value if the given field definition does not allow default values. + if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) { + $field['default'] = null; + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * Build SQL for table options + * + * @param array $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = array(); + + // Charset + if ( ! isset($options['charset'])) { + $options['charset'] = 'utf8'; + } + + $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); + + // Collate + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $tableOptions[] = sprintf('COLLATE %s', $options['collate']); + + // Engine + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableOptions[] = sprintf("COMMENT = %s ", $this->quoteStringLiteral($comment)); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param array $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return (isset($options['partition_options'])) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + foreach ($diff->addedColumns as $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(); + + // Don't propagate default value changes for unsupported column types. + if ($columnDiff->hasChanged('default') && + count($columnDiff->changedProperties) === 1 && + ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) + ) { + continue; + } + + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + if (isset($diff->addedIndexes['primary'])) { + $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns())); + $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; + unset($diff->addedIndexes['primary']); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + // Dropping primary keys requires to unset autoincrement attribute on the particular column first. + if ($remIndex->isPrimary() && $diff->fromTable instanceof Table) { + foreach ($remIndex->getColumns() as $columnName) { + $column = $diff->fromTable->getColumn($columnName); + + if ($column->getAutoincrement() === true) { + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // original autoincrement information might be needed later on by other parts of the table alteration + $column->setAutoincrement(true); + } + } + } + + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + + $indexClause = 'INDEX ' . $addIndex->getName(); + + if ($addIndex->isPrimary()) { + $indexClause = 'PRIMARY KEY'; + } elseif ($addIndex->isUnique()) { + $indexClause = 'UNIQUE INDEX ' . $addIndex->getName(); + } + + $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query .= 'ADD ' . $indexClause; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex->getQuotedColumns($this)) . ')'; + + $sql[] = $query; + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $engine = 'INNODB'; + + if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) { + $engine = strtoupper(trim($diff->fromTable->getOption('engine'))); + } + + // Suppress foreign key constraint propagation on non-supporting engines. + if ('INNODB' !== $engine) { + $diff->addedForeignKeys = array(); + $diff->changedForeignKeys = array(); + $diff->removedForeignKeys = array(); + } + + $sql = array_merge( + $sql, + $this->getPreAlterTableAlterIndexForeignKeySQL($diff), + parent::getPreAlterTableIndexForeignKeySQL($diff), + $this->getPreAlterTableRenameIndexForeignKeySQL($diff) + ); + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->changedIndexes as $changedIndex) { + // Changed primary key + if ($changedIndex->isPrimary() && $diff->fromTable instanceof Table) { + foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName) { + $column = $diff->fromTable->getColumn($columnName); + + // Check if an autoincrement column was dropped from the primary key. + if ($column->getAutoincrement() && ! in_array($columnName, $changedIndex->getColumns())) { + // The autoincrement attribute needs to be removed from the dropped column + // before we can drop and recreate the primary key. + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // Restore the autoincrement attribute as it might be needed later on + // by other parts of the table alteration. + $column->setAutoincrement(true); + } + } + } + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $tableName = $diff->getName($this)->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (! in_array($foreignKey, $diff->changedForeignKeys, true)) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + } + + return $sql; + } + + /** + * Returns the remaining foreign key constraints that require one of the renamed indexes. + * + * "Remaining" here refers to the diff between the foreign keys currently defined in the associated + * table and the foreign keys to be removed. + * + * @param TableDiff $diff The table diff to evaluate. + * + * @return array + */ + private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff) + { + if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) { + return array(); + } + + $foreignKeys = array(); + /** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] $remainingForeignKeys */ + $remainingForeignKeys = array_diff_key( + $diff->fromTable->getForeignKeys(), + $diff->removedForeignKeys + ); + + foreach ($remainingForeignKeys as $foreignKey) { + foreach ($diff->renamedIndexes as $index) { + if ($foreignKey->intersectsIndexColumns($index)) { + $foreignKeys[] = $foreignKey; + + break; + } + } + } + + return $foreignKeys; + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge( + parent::getPostAlterTableIndexForeignKeySQL($diff), + $this->getPostAlterTableRenameIndexForeignKeySQL($diff) + ); + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $tableName = (false !== $diff->newName) + ? $diff->getNewName()->getQuotedName($this) + : $diff->getName($this)->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (! in_array($foreignKey, $diff->changedForeignKeys, true)) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } elseif ($index->hasFlag('fulltext')) { + $type .= 'FULLTEXT '; + } elseif ($index->hasFlag('spatial')) { + $type .= 'SPATIAL '; + } + + return $type; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + $unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : ''; + + return $unsigned . $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } elseif (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); + } elseif (!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 string $table + * + * @return string + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mysql'; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'LOCK IN SHARE MODE'; + } + + /** + * {@inheritDoc} + */ + 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', + 'binary' => 'binary', + 'varbinary' => 'binary', + 'set' => 'simple_array', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 65535; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords'; + } + + /** + * {@inheritDoc} + * + * MySQL commits a transaction implicitly when DROP TABLE is executed, however not + * if DROP TEMPORARY TABLE is executed. + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!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. + * TINYBLOB : 2 ^ 8 - 1 = 255 + * BLOB : 2 ^ 16 - 1 = 65535 + * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 + * LONGBLOB : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getBlobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYBLOB) { + return 'TINYBLOB'; + } + + if ($length <= static::LENGTH_LIMIT_BLOB) { + return 'BLOB'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { + return 'MEDIUMBLOB'; + } + } + + 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 00000000000..a58c747e929 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -0,0 +1,1123 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Types\BinaryType; + +/** + * OraclePlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + */ +class OraclePlatform extends AbstractPlatform +{ + /** + * Assertion for Oracle identifiers. + * + * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm + * + * @param string $identifier + * + * @throws DBALException + */ + static public function assertValidIdentifier($identifier) + { + if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) { + throw new DBALException("Invalid Oracle identifier"); + } + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return "SUBSTR($value, $position, $length)"; + } + + return "SUBSTR($value, $position)"; + } + + /** + * {@inheritDoc} + */ + 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\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'INSTR('.$str.', '.$substr.')'; + } + + return 'INSTR('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'SYS_GUID()'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_MONTH: + case self::DATE_INTERVAL_UNIT_QUARTER: + case self::DATE_INTERVAL_UNIT_YEAR: + switch ($unit) { + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + break; + + case self::DATE_INTERVAL_UNIT_YEAR: + $interval *= 12; + break; + } + + return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')'; + + default: + $calculationClause = ''; + + switch ($unit) { + case self::DATE_INTERVAL_UNIT_SECOND: + $calculationClause = '/24/60/60'; + break; + + case self::DATE_INTERVAL_UNIT_MINUTE: + $calculationClause = '/24/60'; + break; + + case self::DATE_INTERVAL_UNIT_HOUR: + $calculationClause = '/24'; + break; + + case self::DATE_INTERVAL_UNIT_WEEK: + $calculationClause = '*7'; + break; + } + + return '(' . $date . $operator . $interval . $calculationClause . ')'; + } + } + + /** + * {@inheritDoc} + * + * 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. + */ + public function getDateDiffExpression($date1, $date2) + { + return "TRUNC(TO_NUMBER(SUBSTR((" . $date1 . "-" . $date2 . "), 1, INSTR(" . $date1 . "-" . $date2 .", ' '))))"; + } + + /** + * {@inheritDoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND('.$value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . '-' . + $this->getBitAndComparisonExpression($value1, $value2) + . '+' . $value2 . ')'; + } + + /** + * {@inheritDoc} + * + * 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()} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() + . $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + * + * @param Sequence $sequence + * + * @return string + */ + private function getSequenceCacheSQL(Sequence $sequence) + { + if ($sequence->getCache() === 0) { + return ' NOCACHE'; + } else if ($sequence->getCache() === 1) { + return ' NOCACHE'; + } else if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.nextval FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + 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); + } + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'NUMBER(1)'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'NUMBER(10)'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(20)'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(5)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)') + : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 2000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT username FROM all_users'; + } + + /** + * {@inheritDoc} + */ + public function getListSequencesSQL($database) + { + $database = $this->normalizeIdentifier($database); + + return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ". + "WHERE SEQUENCE_OWNER = '" . $database->getName() . "'"; + } + + /** + * {@inheritDoc} + */ + 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 $index) { + $sql[] = $this->getCreateIndexSQL($index, $table); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = $this->normalizeIdentifier($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->getName() . "' " . + "ORDER BY uind_col.column_position ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return 'SELECT * FROM sys.user_tables'; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT view_name, text FROM sys.user_views'; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * @param string $name + * @param string $table + * @param integer $start + * + * @return array + */ + public function getCreateAutoincrementSql($name, $table, $start = 1) + { + $tableIdentifier = $this->normalizeIdentifier($table); + $quotedTableName = $tableIdentifier->getQuotedName($this); + $unquotedTableName = $tableIdentifier->getName(); + + $nameIdentifier = $this->normalizeIdentifier($name); + $quotedName = $nameIdentifier->getQuotedName($this); + $unquotedName = $nameIdentifier->getName(); + + $sql = array(); + + $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier); + + $idx = new Index($autoincrementIdentifierName, array($quotedName), true, true); + + $sql[] = 'DECLARE + constraints_Count NUMBER; +BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \'' . $unquotedTableName . '\' AND CONSTRAINT_TYPE = \'P\'; + IF constraints_Count = 0 OR constraints_Count = \'\' THEN + EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $quotedTableName).'\'; + END IF; +END;'; + + $sequenceName = $this->getIdentitySequenceName( + $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName, + $nameIdentifier->isQuoted() ? $quotedName : $unquotedName + ); + $sequence = new Sequence($sequenceName, $start); + $sql[] = $this->getCreateSequenceSQL($sequence); + + $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . ' + BEFORE INSERT + ON ' . $quotedTableName . ' + FOR EACH ROW +DECLARE + last_Sequence NUMBER; + last_InsertID NUMBER; +BEGIN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; + IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.'.$quotedName.' = 0) THEN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; + ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = \'' . $sequence->getName() . '\'; + SELECT :NEW.' . $quotedName . ' 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; + } + + /** + * Returns the SQL statements to drop the autoincrement for the given table name. + * + * @param string $table The table name to drop the autoincrement for. + * + * @return array + */ + public function getDropAutoincrementSql($table) + { + $table = $this->normalizeIdentifier($table); + $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); + $identitySequenceName = $this->getIdentitySequenceName( + $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), + '' + ); + + return array( + 'DROP TRIGGER ' . $autoincrementIdentifierName, + $this->getDropSequenceSQL($identitySequenceName), + $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)), + ); + } + + /** + * Normalizes the given identifier. + * + * Uppercases the given identifier if it is not quoted by intention + * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers. + * + * @param string $name The identifier to normalize. + * + * @return Identifier The normalized identifier. + */ + private function normalizeIdentifier($name) + { + $identifier = new Identifier($name); + + return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name)); + } + + /** + * Returns the autoincrement primary key identifier name for the given table identifier. + * + * Quotes the autoincrement primary key identifier name + * if the given table name is quoted by intention. + * + * @param Identifier $table The table identifier to return the autoincrement primary key identifier name for. + * + * @return string + */ + private function getAutoincrementIdentifierName(Identifier $table) + { + $identifierName = $table->getName() . '_AI_PK'; + + return $table->isQuoted() + ? $this->quoteSingleIdentifier($identifierName) + : $identifierName; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table) + { + $table = $table = $this->normalizeIdentifier($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->getName() . "' + ORDER BY alc.constraint_name ASC, cols.position ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = $this->normalizeIdentifier($table); + + return "SELECT * FROM user_constraints WHERE table_name = '" . $table->getName() . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $table = $this->normalizeIdentifier($table); + + $tabColumnsTableName = "user_tab_columns"; + $colCommentsTableName = "user_col_comments"; + $ownerCondition = ''; + + if (null !== $database) { + $database = $this->normalizeIdentifier($database); + $tabColumnsTableName = "all_tab_columns"; + $colCommentsTableName = "all_col_comments"; + $ownerCondition = "AND c.owner = '" . $database->getName() . "'"; + } + + return "SELECT c.*, d.comments FROM $tabColumnsTableName c ". + "INNER JOIN " . $colCommentsTableName . " d ON d.TABLE_NAME = c.TABLE_NAME AND d.COLUMN_NAME = c.COLUMN_NAME ". + "WHERE c.table_name = '" . $table->getName() . "' ".$ownerCondition." ORDER BY c.column_name"; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritdoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $referentialAction = null; + + if ($foreignKey->hasOption('onDelete')) { + $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + + return $referentialAction ? ' ON DELETE ' . $referentialAction : ''; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + $action = strtoupper($action); + + switch ($action) { + case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION. + case 'NO ACTION': + // NO ACTION cannot be declared explicitly, + // therefore returning empty string to indicate to OMIT the referential clause. + return ''; + + case 'CASCADE': + case 'SET NULL': + return $action; + + default: + // SET DEFAULT is not supported, throw exception instead. + throw new \InvalidArgumentException('Invalid foreign key action: ' . $action); + } + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return 'DROP USER ' . $database . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + 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->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')'; + } + + $fields = array(); + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + + // Do not generate column alteration clause if type is binary and only fixed property has changed. + // Oracle only supports binary type columns with variable length. + // Avoids unnecessary table alteration statements. + if ($column->getType() instanceof BinaryType && + $columnDiff->hasChanged('fixed') && + count($columnDiff->changedProperties) === 1 + ) { + continue; + } + + $columnHasChangedComment = $columnDiff->hasChanged('comment'); + + /** + * Do not add query part if only comment has changed + */ + if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) { + $columnInfo = $column->toArray(); + + if ( ! $columnDiff->hasChanged('notnull')) { + unset($columnInfo['notnull']); + } + + $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo); + } + + if ($columnHasChangedComment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')'; + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . + ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' 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->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields).')'; + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritdoc} + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $default = $this->getDefaultValueDeclarationSQL($field); + + $notnull = ''; + + if (isset($field['notnull'])) { + $notnull = $field['notnull'] ? ' NOT NULL' : ' 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 . $default . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function usesSequenceEmulatedIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getIdentitySequenceName($tableName, $columnName) + { + $table = new Identifier($tableName); + + // No usage of column name to preserve BC compatibility with <2.5 + $identitySequenceName = $table->getName() . '_SEQ'; + + if ($table->isQuoted()) { + $identitySequenceName = '"' . $identitySequenceName . '"'; + } + + $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName); + + return $identitySequenceIdentifier->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'oracle'; + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + * + * Oracle returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sP'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d 00:00:00'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return '1900-01-01 H:i:s'; + } + + /** + * {@inheritDoc} + */ + public function fixSchemaElementName($schemaElementName) + { + if (strlen($schemaElementName) > 30) { + // Trim it + return substr($schemaElementName, 0, 30); + } + + return $schemaElementName; + } + + /** + * {@inheritDoc} + */ + public function getMaxIdentifierLength() + { + return 30; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyOnUpdate() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + 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', + 'binary_float' => 'float', + 'binary_double' => 'float', + 'long' => 'string', + 'clob' => 'text', + 'nclob' => 'text', + 'raw' => 'binary', + 'long raw' => 'blob', + 'rowid' => 'string', + 'urowid' => 'string', + 'blob' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php new file mode 100644 index 00000000000..b14aa7301ed --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.1 database platform. + * + * @author Martin Hasoň + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL91Platform extends PostgreSqlPlatform +{ + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords'; + } + + /** + * {@inheritDoc} + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $this->quoteSingleIdentifier($collation); + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $sql = parent::getListTableColumnsSQL($table, $database); + $parts = explode('AS complete_type,', $sql, 2); + + return $parts[0].'AS complete_type, (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,'.$parts[1]; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php new file mode 100644 index 00000000000..19cf8035630 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.2 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL92Platform extends PostgreSQL91Platform +{ + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return 'JSON'; + } + + /** + * {@inheritdoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SMALLSERIAL'; + } + + return parent::getSmallIntTypeDeclarationSQL($field); + } + + /** + * {@inheritdoc} + */ + public function hasNativeJsonType() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords'; + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['json'] = 'json_array'; + } + + /** + * {@inheritdoc} + */ + public function getCloseActiveDatabaseConnectionsSQL($database) + { + return "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database'"; + } +} 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 00000000000..681eb59f0d6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -0,0 +1,1168 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\BinaryType; +use Doctrine\DBAL\Types\BlobType; + +/** + * PostgreSqlPlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Rename: PostgreSQLPlatform + */ +class PostgreSqlPlatform extends AbstractPlatform +{ + /** + * @var bool + */ + private $useBooleanTrueFalseStrings = true; + + /** + * @var array PostgreSQL booleans literals + */ + private $booleanLiterals = array( + 'true' => array( + 't', + 'true', + 'y', + 'yes', + 'on', + '1' + ), + 'false' => array( + 'f', + 'false', + 'n', + 'no', + 'off', + '0' + ) + ); + + /** + * PostgreSQL has different behavior with some drivers + * with regard to how booleans have to be handled. + * + * Enables use of 'true'/'false' or otherwise 1 and 0 instead. + * + * @param bool $flag + */ + public function setUseBooleanTrueFalseStrings($flag) + { + $this->useBooleanTrueFalseStrings = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression() + { + return 'LOCALTIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'SIMILAR TO'; + } + + /** + * {@inheritDoc} + */ + 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'; + } + + return 'POSITION('.$substr.' IN '.$str.')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + if (self::DATE_INTERVAL_UNIT_QUARTER === $unit) { + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + } + + return "(" . $date ." " . $operator . " (" . $interval . " || ' " . $unit . "')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'public'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsPartialIndexes() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function usesSequenceEmulatedIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getIdentitySequenceName($tableName, $columnName) + { + return $tableName . '_' . $columnName . '_seq'; + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT datname FROM pg_database'; + } + + /** + * {@inheritDoc} + */ + public function getListNamespacesSQL() + { + return "SELECT schema_name AS nspname + FROM information_schema.schemata + WHERE schema_name NOT LIKE 'pg_%' + AND schema_name != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListSequencesSQL($database) + { + return "SELECT sequence_name AS relname, + sequence_schema AS schemaname + FROM information_schema.sequences + WHERE sequence_schema NOT LIKE 'pg_%' + AND sequence_schema != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT quote_ident(table_name) AS table_name, + table_schema AS schema_name + FROM information_schema.tables + WHERE table_schema NOT LIKE 'pg_%' + AND table_schema != 'information_schema' + AND table_name != 'geometry_columns' + AND table_name != 'spatial_ref_sys' + AND table_type != 'VIEW'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT quote_ident(table_name) AS viewname, + table_schema AS schemaname, + view_definition AS definition + FROM information_schema.views + WHERE view_definition IS NOT NULL'; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT quote_ident(r.conname) as 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'"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = new Identifier($table); + $table = $table->getName(); + + return "SELECT + quote_ident(relname) as 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') + )"; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid, + pg_get_expr(indpred, indrelid) AS where + 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 replace(replace(setting,'\"\$user\"',user),' ','') from pg_catalog.pg_settings where name = 'search_path'),','))"; + } + + $table = new Identifier($table); + $whereClause .= "$classAlias.relname = '" . $table->getName() . "' AND $namespaceAlias.nspname = $schema"; + + return $whereClause; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT + a.attnum, + quote_ident(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.oid = a.atttypid) 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_get_expr(adbin, adrelid) + 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"; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * Returns the SQL statement for disallowing new connections on the given database. + * + * This is useful to force DROP DATABASE operations which could fail because of active connections. + * + * @param string $database The name of the database to disallow new connections for. + * + * @return string + */ + public function getDisallowDatabaseConnectionsSQL($database) + { + return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = '$database'"; + } + + /** + * Returns the SQL statement for closing currently active connections on the given database. + * + * This is useful to force DROP DATABASE operations which could fail because of active connections. + * + * @param string $database The name of the database to close currently active connections for. + * + * @return string + */ + public function getCloseActiveDatabaseConnectionsSQL($database) + { + return "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = '$database'"; + } + + /** + * {@inheritDoc} + */ + 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) + || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) + ) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + 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->getName($this)->getQuotedName($this) . ' ' . $query; + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + foreach ($diff->changedColumns as $columnDiff) { + /** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if ($this->isUnchangedBinaryColumn($columnDiff)) { + continue; + } + + $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); + $column = $columnDiff->column; + + if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('precision') || $columnDiff->hasChanged('scale') || $columnDiff->hasChanged('fixed')) { + $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->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('default') || $columnDiff->hasChanged('type')) { + $defaultClause = null === $column->getDefault() + ? ' DROP DEFAULT' + : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); + $query = 'ALTER ' . $oldColumnName . $defaultClause; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotNull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('autoincrement')) { + if ($column->getAutoincrement()) { + // add autoincrement + $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); + + $sql[] = "CREATE SEQUENCE " . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->getName($this)->getQuotedName($this) . "))"; + $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $sql[] = "ALTER TABLE " . $diff->getName($this)->getQuotedName($this) . " " . $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->getName($this)->getQuotedName($this) . " " . $query; + } + } + + if ($columnDiff->hasChanged('comment')) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + + if ($columnDiff->hasChanged('length')) { + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $column->getType()->getSqlDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . + ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Checks whether a given column diff is a logically unchanged binary type column. + * + * Used to determine whether a column alteration for a binary type column can be skipped. + * Doctrine's {@link \Doctrine\DBAL\Types\BinaryType} and {@link \Doctrine\DBAL\Types\BlobType} + * are mapped to the same database column type on this platform as this platform + * does not have a native VARBINARY/BINARY column type. Therefore the {@link \Doctrine\DBAL\Schema\Comparator} + * might detect differences for binary type columns which do not have to be propagated + * to database as there actually is no difference at database level. + * + * @param ColumnDiff $columnDiff The column diff to check against. + * + * @return boolean True if the given column diff is an unchanged binary type column, false otherwise. + */ + private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) + { + $columnType = $columnDiff->column->getType(); + + if ( ! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { + return false; + } + + $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null; + + if ($fromColumn) { + $fromColumnType = $fromColumn->getType(); + + if ( ! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, array('type', 'length', 'fixed'))) === 0; + } + + if ($columnDiff->hasChanged('type')) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, array('length', 'fixed'))) === 0; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) . + " IS $comment"; + } + + /** + * {@inheritDoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + * + * @param Sequence $sequence + * + * @return string + */ + private function getSequenceCacheSQL(Sequence $sequence) + { + if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + return $this->getDropConstraintSQL($foreignKey, $table); + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * Converts a single boolean value. + * + * First converts the value to its native PHP boolean type + * and passes it to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $value The value to convert. + * @param callable $callback The callback function to use for converting the real boolean value. + * + * @return mixed + * @throws \UnexpectedValueException + */ + private function convertSingleBooleanValue($value, $callback) + { + if (null === $value) { + return $callback(null); + } + + if (is_bool($value) || is_numeric($value)) { + return $callback($value ? true : false); + } + + if (!is_string($value)) { + return $callback(true); + } + + /** + * Better safe than sorry: http://php.net/in_array#106319 + */ + if (in_array(trim(strtolower($value)), $this->booleanLiterals['false'], true)) { + return $callback(false); + } + + if (in_array(trim(strtolower($value)), $this->booleanLiterals['true'], true)) { + return $callback(true); + } + + throw new \UnexpectedValueException("Unrecognized boolean literal '${value}'"); + } + + /** + * Converts one or multiple boolean values. + * + * First converts the value(s) to their native PHP boolean type + * and passes them to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $item The value(s) to convert. + * @param callable $callback The callback function to use for converting the real boolean value(s). + * + * @return mixed + */ + private function doConvertBooleans($item, $callback) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + $item[$key] = $this->convertSingleBooleanValue($value, $callback); + } + + return $item; + } + + return $this->convertSingleBooleanValue($item, $callback); + } + + /** + * {@inheritDoc} + * + * Postgres wants boolean values converted to the strings 'true'/'false'. + */ + public function convertBooleans($item) + { + if ( ! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleans($item); + } + + return $this->doConvertBooleans( + $item, + function ($boolean) { + if (null === $boolean) { + return 'NULL'; + } + + return true === $boolean ? 'true' : 'false'; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertBooleansToDatabaseValue($item) + { + if ( ! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleansToDatabaseValue($item); + } + + return $this->doConvertBooleans( + $item, + function ($boolean) { + return null === $boolean ? null : (int) $boolean; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertFromBoolean($item) + { + if (in_array(strtolower($item), $this->booleanLiterals['false'], true)) { + return false; + } + + return parent::convertFromBoolean($item); + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return "SELECT NEXTVAL('" . $sequenceName . "')"; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' + . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SERIAL'; + } + + return 'INT'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'BIGSERIAL'; + } + + return 'BIGINT'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UUID'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID_GENERATE_V4()'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BYTEA'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'postgresql'; + } + + /** + * {@inheritDoc} + * + * PostgreSQL returns all column names in SQL result sets in lowercase. + */ + public function getSQLResultCasing($column) + { + return strtolower($column); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sO'; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE '.$tableName.' '.(($cascade)?'CASCADE':''); + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'FOR SHARE'; + } + + /** + * {@inheritDoc} + */ + 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', + 'inet' => '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', + 'uuid' => 'guid', + 'bytea' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 0; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BYTEA'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php new file mode 100644 index 00000000000..44ba707b617 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * The SQLAnywhere11Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 11 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere11Platform extends SQLAnywherePlatform +{ + /** + * {@inheritdoc} + */ + public function getRegexpExpression() + { + return 'REGEXP'; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php new file mode 100644 index 00000000000..17b413eccb3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; + +/** + * The SQLAnywhere12Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 12 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere12Platform extends SQLAnywhere11Platform +{ + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.uP'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP WITH TIME ZONE'; + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.NEXTVAL'; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + if ( ! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) { + return ' WITH NULLS NOT DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); + } + + return parent::getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['timestamp with time zone'] = 'datetime'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php new file mode 100644 index 00000000000..6808d7301a7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\DBAL\Schema\Index; + +/** + * The SQLAnywhere16Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 16 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere16Platform extends SQLAnywhere12Platform +{ + /** + * {@inheritdoc} + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + if ($index->hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) { + throw new UnexpectedValueException( + 'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.' + ); + } + + if ( ! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) { + return ' WITH NULLS DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); + } + + return parent::getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php new file mode 100644 index 00000000000..36debc390e1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php @@ -0,0 +1,1480 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 10 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywherePlatform extends AbstractPlatform +{ + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_SIMPLE = 1; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_FULL = 2; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_FULL_UNIQUE = 130; + + /** + * {@inheritdoc} + */ + public function appendLockHint($fromClause, $lockMode) + { + switch (true) { + case $lockMode === LockMode::NONE: + return $fromClause . ' WITH (NOLOCK)'; + + case $lockMode === LockMode::PESSIMISTIC_READ: + return $fromClause . ' WITH (UPDLOCK)'; + + case $lockMode === LockMode::PESSIMISTIC_WRITE: + return $fromClause . ' WITH (XLOCK)'; + + default: + return $fromClause; + } + } + + /** + * {@inheritdoc} + * + * SQL Anywhere supports a maximum length of 128 bytes for identifiers. + */ + public function fixSchemaElementName($schemaElementName) + { + $maxIdentifierLength = $this->getMaxIdentifierLength(); + + if (strlen($schemaElementName) > $maxIdentifierLength) { + return substr($schemaElementName, 0, $maxIdentifierLength); + } + + return $schemaElementName; + } + + /** + * {@inheritdoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match')); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('check_on_commit') && (boolean) $foreignKey->getOption('check_on_commit')) { + $query .= ' CHECK ON COMMIT'; + } + + if ($foreignKey->hasOption('clustered') && (boolean) $foreignKey->getOption('clustered')) { + $query .= ' CLUSTERED'; + } + + if ($foreignKey->hasOption('for_olap_workload') && (boolean) $foreignKey->getOption('for_olap_workload')) { + $query .= ' FOR OLAP WORKLOAD'; + } + + return $query; + } + + /** + * {@inheritdoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + $commentsSQL = array(); + $tableSql = array(); + $alterClauses = array(); + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $alterClauses[] = $this->getAlterTableAddColumnClause($column); + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $alterClauses[] = $this->getAlterTableRemoveColumnClause($column); + } + + /** @var \Doctrine\DBAL\Schema\ColumnDiff $columnDiff */ + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $alterClause = $this->getAlterTableChangeColumnClause($columnDiff); + + if (null !== $alterClause) { + $alterClauses[] = $alterClause; + } + + if ($columnDiff->hasChanged('comment')) { + $column = $columnDiff->column; + + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . + $this->getAlterTableRenameColumnClause($oldColumnName, $column); + } + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ( ! empty($alterClauses)) { + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(", ", $alterClauses); + } + + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . + $this->getAlterTableRenameTableClause($diff->getNewName()); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Returns the SQL clause for creating a column in a table alteration. + * + * @param Column $column The column to add. + * + * @return string + */ + protected function getAlterTableAddColumnClause(Column $column) + { + return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + /** + * Returns the SQL clause for altering a table. + * + * @param Identifier $tableName The quoted name of the table to alter. + * + * @return string + */ + protected function getAlterTableClause(Identifier $tableName) + { + return 'ALTER TABLE ' . $tableName->getQuotedName($this); + } + + /** + * Returns the SQL clause for dropping a column in a table alteration. + * + * @param Column $column The column to drop. + * + * @return string + */ + protected function getAlterTableRemoveColumnClause(Column $column) + { + return 'DROP ' . $column->getQuotedName($this); + } + + /** + * Returns the SQL clause for renaming a column in a table alteration. + * + * @param string $oldColumnName The quoted name of the column to rename. + * @param Column $column The column to rename to. + * + * @return string + */ + protected function getAlterTableRenameColumnClause($oldColumnName, Column $column) + { + $oldColumnName = new Identifier($oldColumnName); + + return 'RENAME ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this); + } + + /** + * Returns the SQL clause for renaming a table in a table alteration. + * + * @param Identifier $newTableName The quoted name of the table to rename to. + * + * @return string + */ + protected function getAlterTableRenameTableClause(Identifier $newTableName) + { + return 'RENAME ' . $newTableName->getQuotedName($this); + } + + /** + * Returns the SQL clause for altering a column in a table alteration. + * + * This method returns null in case that only the column comment has changed. + * Changes in column comments have to be handled differently. + * + * @param ColumnDiff $columnDiff The diff of the column to alter. + * + * @return string|null + */ + protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff) + { + $column = $columnDiff->column; + + // Do not return alter clause if only comment has changed. + if ( ! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) { + $columnAlterationClause = 'ALTER ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + if ($columnDiff->hasChanged('default') && null === $column->getDefault()) { + $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT'; + } + + return $columnAlterationClause; + } + } + + /** + * {@inheritdoc} + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'BIGINT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 32767; + } + + /** + * {@inheritdoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'LONG BINARY'; + } + + /** + * {@inheritdoc} + * + * BIT type columns require an explicit NULL declaration + * in SQL Anywhere if they shall be nullable. + * Otherwise by just omitting the NOT NULL clause, + * SQL Anywhere will declare them NOT NULL nonetheless. + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + $nullClause = isset($columnDef['notnull']) && (boolean) $columnDef['notnull'] === false ? ' NULL' : ''; + + return 'BIT' . $nullClause; + } + + /** + * {@inheritdoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . '.' . $columnName->getQuotedName($this) . + " IS $comment"; + } + + /** + * {@inheritdoc} + */ + public function getConcatExpression() + { + return 'STRING(' . implode(', ', (array) func_get_args()) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($constraint instanceof ForeignKeyConstraint) { + return $this->getCreateForeignKeySQL($constraint, $table); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . + ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this)); + } + + /** + * {@inheritdoc} + */ + public function getCreateDatabaseSQL($database) + { + $database = new Identifier($database); + + return "CREATE DATABASE '" . $database->getName() . "'"; + } + + /** + * {@inheritdoc} + * + * Appends SQL Anywhere specific flags if given. + */ + public function getCreateIndexSQL(Index $index, $table) + { + return parent::getCreateIndexSQL($index, $table). $this->getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index); + } + + /** + * {@inheritdoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE'; + } + + /** + * {@inheritdoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritdoc} + */ + public function getCurrentDateSQL() + { + return 'CURRENT DATE'; + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimeSQL() + { + return 'CURRENT TIME'; + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT TIMESTAMP'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ('-' === $operator) { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * {@inheritdoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritdoc} + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_READ_UNCOMMITTED; + } + + /** + * {@inheritdoc} + */ + public function getDropDatabaseSQL($database) + { + $database = new Identifier($database); + + return "DROP DATABASE '" . $database->getName() . "'"; + } + + /** + * {@inheritdoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } + + if ( ! is_string($index)) { + throw new \InvalidArgumentException( + 'SQLAnywherePlatform::getDropIndexSQL() expects $index parameter to be string or ' . + '\Doctrine\DBAL\Schema\Index.' + ); + } + + if ( ! isset($table)) { + return 'DROP INDEX ' . $index; + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + if ( ! is_string($table)) { + throw new \InvalidArgumentException( + 'SQLAnywherePlatform::getDropIndexSQL() expects $table parameter to be string or ' . + '\Doctrine\DBAL\Schema\Table.' + ); + } + + return 'DROP INDEX ' . $table . '.' . $index; + } + + /** + * {@inheritdoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = ''; + $foreignKeyName = $foreignKey->getName(); + $localColumns = $foreignKey->getQuotedLocalColumns($this); + $foreignColumns = $foreignKey->getQuotedForeignColumns($this); + $foreignTableName = $foreignKey->getQuotedForeignTableName($this); + + if ( ! empty($foreignKeyName)) { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } + + if (empty($localColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'local' required."); + } + + if (empty($foreignColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); + } + + if (empty($foreignTableName)) { + throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); + } + + if ($foreignKey->hasOption('notnull') && (boolean) $foreignKey->getOption('notnull')) { + $sql .= 'NOT NULL '; + } + + return $sql . + 'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' . + 'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) . + ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')'; + } + + /** + * Returns foreign key MATCH clause for given type. + * + * @param integer $type The foreign key match type + * + * @return string + * + * @throws \InvalidArgumentException if unknown match type given + */ + public function getForeignKeyMatchClauseSQL($type) + { + switch ((int) $type) { + case self::FOREIGN_KEY_MATCH_SIMPLE: + return 'SIMPLE'; + break; + case self::FOREIGN_KEY_MATCH_FULL: + return 'FULL'; + break; + case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE: + return 'UNIQUE SIMPLE'; + break; + case self::FOREIGN_KEY_MATCH_FULL_UNIQUE: + return 'UNIQUE FULL'; + default: + throw new \InvalidArgumentException('Invalid foreign key match type: ' . $type); + } + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + // NO ACTION is not supported, therefore falling back to RESTRICT. + if (strtoupper($action) === 'NO ACTION') { + return 'RESTRICT'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + /** + * {@inheritdoc} + */ + public function getForUpdateSQL() + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function getGuidExpression() + { + return 'NEWID()'; + } + + /** + * {@inheritdoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritdoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + // Index declaration in statements like CREATE TABLE is not supported. + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritdoc} + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'INT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritdoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT db_name(number) AS name FROM sa_db_list()'; + } + + /** + * {@inheritdoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $user = 'USER_NAME()'; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT col.column_name, + COALESCE(def.user_type_name, def.domain_name) AS 'type', + def.declared_width AS 'length', + def.scale, + CHARINDEX('unsigned', def.domain_name) AS 'unsigned', + IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull', + col.\"default\", + def.is_autoincrement AS 'autoincrement', + rem.remarks AS 'comment' + FROM sa_describe_query('SELECT * FROM \"$table\"') AS def + JOIN SYS.SYSTABCOL AS col + ON col.table_id = def.base_table_id AND col.column_id = def.base_column_id + LEFT JOIN SYS.SYSREMARK AS rem + ON col.object_id = rem.object_id + WHERE def.base_owner_name = $user + ORDER BY def.base_column_id ASC"; + } + + /** + * {@inheritdoc} + * + * @todo Where is this used? Which information should be retrieved? + */ + public function getListTableConstraintsSQL($table) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT con.* + FROM SYS.SYSCONSTRAINT AS con + JOIN SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id + WHERE tab.table_name = '$table' + AND tab.creator = USER_ID($user)"; + } + + /** + * {@inheritdoc} + */ + public function getListTableForeignKeysSQL($table) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT fcol.column_name AS local_column, + ptbl.table_name AS foreign_table, + pcol.column_name AS foreign_column, + idx.index_name, + IF fk.nulls = 'N' + THEN 1 + ELSE NULL + ENDIF AS notnull, + CASE ut.referential_action + WHEN 'C' THEN 'CASCADE' + WHEN 'D' THEN 'SET DEFAULT' + WHEN 'N' THEN 'SET NULL' + WHEN 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_update, + CASE dt.referential_action + WHEN 'C' THEN 'CASCADE' + WHEN 'D' THEN 'SET DEFAULT' + WHEN 'N' THEN 'SET NULL' + WHEN 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_delete, + IF fk.check_on_commit = 'Y' + THEN 1 + ELSE NULL + ENDIF AS check_on_commit, -- check_on_commit flag + IF ftbl.clustered_index_id = idx.index_id + THEN 1 + ELSE NULL + ENDIF AS 'clustered', -- clustered flag + IF fk.match_type = 0 + THEN NULL + ELSE fk.match_type + ENDIF AS 'match', -- match option + IF pidx.max_key_distance = 1 + THEN 1 + ELSE NULL + ENDIF AS for_olap_workload -- for_olap_workload flag + FROM SYS.SYSFKEY AS fk + JOIN SYS.SYSIDX AS idx + ON fk.foreign_table_id = idx.table_id + AND fk.foreign_index_id = idx.index_id + JOIN SYS.SYSPHYSIDX pidx + ON idx.table_id = pidx.table_id + AND idx.phys_index_id = pidx.phys_index_id + JOIN SYS.SYSTAB AS ptbl + ON fk.primary_table_id = ptbl.table_id + JOIN SYS.SYSTAB AS ftbl + ON fk.foreign_table_id = ftbl.table_id + JOIN SYS.SYSIDXCOL AS idxcol + ON idx.table_id = idxcol.table_id + AND idx.index_id = idxcol.index_id + JOIN SYS.SYSTABCOL AS pcol + ON ptbl.table_id = pcol.table_id + AND idxcol.primary_column_id = pcol.column_id + JOIN SYS.SYSTABCOL AS fcol + ON ftbl.table_id = fcol.table_id + AND idxcol.column_id = fcol.column_id + LEFT JOIN SYS.SYSTRIGGER ut + ON fk.foreign_table_id = ut.foreign_table_id + AND fk.foreign_index_id = ut.foreign_key_id + AND ut.event = 'C' + LEFT JOIN SYS.SYSTRIGGER dt + ON fk.foreign_table_id = dt.foreign_table_id + AND fk.foreign_index_id = dt.foreign_key_id + AND dt.event = 'D' + WHERE ftbl.table_name = '$table' + AND ftbl.creator = USER_ID($user) + ORDER BY fk.foreign_index_id ASC, idxcol.sequence ASC"; + } + + /** + * {@inheritdoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT idx.index_name AS key_name, + IF idx.index_category = 1 + THEN 1 + ELSE 0 + ENDIF AS 'primary', + col.column_name, + IF idx.\"unique\" IN(1, 2, 5) + THEN 0 + ELSE 1 + ENDIF AS non_unique, + IF tbl.clustered_index_id = idx.index_id + THEN 1 + ELSE NULL + ENDIF AS 'clustered', -- clustered flag + IF idx.\"unique\" = 5 + THEN 1 + ELSE NULL + ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag + IF pidx.max_key_distance = 1 + THEN 1 + ELSE NULL + ENDIF AS for_olap_workload -- for_olap_workload flag + FROM SYS.SYSIDX AS idx + JOIN SYS.SYSPHYSIDX pidx + ON idx.table_id = pidx.table_id + AND idx.phys_index_id = pidx.phys_index_id + JOIN SYS.SYSIDXCOL AS idxcol + ON idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id + JOIN SYS.SYSTABCOL AS col + ON idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id + JOIN SYS.SYSTAB AS tbl + ON idx.table_id = tbl.table_id + WHERE tbl.table_name = '$table' + AND tbl.creator = USER_ID($user) + AND idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints + ORDER BY idx.index_id ASC, idxcol.sequence ASC"; + } + + /** + * {@inheritdoc} + */ + public function getListTablesSQL() + { + return "SELECT tbl.table_name + FROM SYS.SYSTAB AS tbl + JOIN SYS.SYSUSER AS usr ON tbl.creator = usr.user_id + JOIN dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id + WHERE tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP' + AND usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users + AND obj.type = 'U' -- user created tables only + ORDER BY tbl.table_name ASC"; + } + + /** + * {@inheritdoc} + * + * @todo Where is this used? Which information should be retrieved? + */ + public function getListUsersSQL() + { + return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC'; + } + + /** + * {@inheritdoc} + */ + public function getListViewsSQL($database) + { + return "SELECT tbl.table_name, v.view_def + FROM SYS.SYSVIEW v + JOIN SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id + JOIN SYS.SYSUSER usr ON tbl.creator = usr.user_id + JOIN dbo.SYSOBJECTS obj ON tbl.object_id = obj.id + WHERE usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users + ORDER BY tbl.table_name ASC"; + } + + /** + * {@inheritdoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $str . ', ' . $substr . ')'; + } + + return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')'; + } + + /** + * {@inheritdoc} + */ + public function getMaxIdentifierLength() + { + return 128; + } + + /** + * {@inheritdoc} + */ + public function getMd5Expression($column) + { + return "HASH(" . $column . ", 'MD5')"; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlanywhere'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a primary key + * declaration to be used in statements like ALTER TABLE. + * + * @param Index $index Index definition + * @param string $name Name of the primary key + * + * @return string DBMS specific SQL code portion needed to set a primary key + * + * @throws \InvalidArgumentException if the given index is not a primary key. + */ + public function getPrimaryKeyDeclarationSQL(Index $index, $name = null) + { + if ( ! $index->isPrimary()) { + throw new \InvalidArgumentException( + 'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()' + ); + } + + return $this->getTableConstraintDeclarationSQL($index, $name); + } + + /** + * {@inheritdoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritdoc} + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'SMALLINT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * Returns the SQL statement for starting an existing database. + * + * In SQL Anywhere you can start and stop databases on a + * database server instance. + * This is a required statement after having created a new database + * as it has to be explicitly started to be usable. + * SQL Anywhere does not automatically start a database after creation! + * + * @param string $database Name of the database to start. + * + * @return string + */ + public function getStartDatabaseSQL($database) + { + $database = new Identifier($database); + + return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF"; + } + + /** + * Returns the SQL statement for stopping a running database. + * + * In SQL Anywhere you can start and stop databases on a + * database server instance. + * This is a required statement before dropping an existing database + * as it has to be explicitly stopped before it can be dropped. + * + * @param string $database Name of the database to stop. + * + * @return string + */ + public function getStopDatabaseSQL($database) + { + $database = new Identifier($database); + + return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY'; + } + + /** + * {@inheritdoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if (null === $length) { + return 'SUBSTRING(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + /** + * {@inheritdoc} + */ + public function getTemporaryTableSQL() + { + return 'GLOBAL TEMPORARY'; + } + + /** + * {@inheritdoc} + */ + public function getTimeFormatString() + { + return 'H:i:s.u'; + } + + /** + * {@inheritdoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritdoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + if ( ! $char) { + switch ($pos) { + case self::TRIM_LEADING: + return $this->getLtrimExpression($str); + case self::TRIM_TRAILING: + return $this->getRtrimExpression($str); + default: + return 'TRIM(' . $str . ')'; + } + } + + $pattern = "'%[^' + $char + ']%'"; + + switch ($pos) { + case self::TRIM_LEADING: + return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))'; + case self::TRIM_TRAILING: + return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))'; + default: + return + 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' . + 'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))'; + } + } + + /** + * {@inheritdoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE ' . $tableName; + } + + /** + * {@inheritdoc} + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + if ($index->isPrimary()) { + throw new \InvalidArgumentException( + 'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().' + ); + } + + if ( ! $index->isUnique()) { + throw new \InvalidArgumentException( + 'Can only create unique constraint declarations, no common index declarations with ' . + 'getUniqueConstraintDeclarationSQL().' + ); + } + + return $this->getTableConstraintDeclarationSQL($index, $name); + } + + /** + * {@inheritdoc} + */ + public function getVarcharDefaultLength() + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function getVarcharMaxLength() + { + return 32767; + } + + /** + * {@inheritdoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $unsigned = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : ''; + $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : ''; + + return $unsigned . $columnDef['integer_type'] . $autoincrement; + } + + /** + * {@inheritdoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + $indexSql = array(); + + if ( ! empty($options['uniqueConstraints'])) { + foreach ((array) $options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if ( ! empty($options['indexes'])) { + /** @var \Doctrine\DBAL\Schema\Index $index */ + foreach ((array) $options['indexes'] as $index) { + $indexSql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if ( ! empty($options['primary'])) { + $flags = ''; + + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) { + $flags = ' CLUSTERED '; + } + + $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')'; + } + + if ( ! empty($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition); + } + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + $check = $this->getCheckDeclarationSQL($columns); + + if ( ! empty($check)) { + $query .= ', ' . $check; + } + + $query .= ')'; + + return array_merge(array($query), $indexSql); + } + + /** + * {@inheritdoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case Connection::TRANSACTION_READ_UNCOMMITTED: + return 0; + case Connection::TRANSACTION_READ_COMMITTED: + return 1; + case Connection::TRANSACTION_REPEATABLE_READ: + return 2; + case Connection::TRANSACTION_SERIALIZABLE: + return 3; + default: + throw new \InvalidArgumentException('Invalid isolation level:' . $level); + } + } + + /** + * {@inheritdoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + $limitOffsetClause = ''; + + if ($limit > 0) { + $limitOffsetClause = 'TOP ' . $limit . ' '; + } + + if ($offset > 0) { + if ($limit == 0) { + $limitOffsetClause = 'TOP ALL '; + } + + $limitOffsetClause .= 'START AT ' . ($offset + 1) . ' '; + } + + if ($limitOffsetClause) { + return preg_replace('/^\s*(SELECT\s+(DISTINCT\s+)?)/i', '\1' . $limitOffsetClause, $query); + } + + return $query; + } + + /** + * Return the INDEX query section dealing with non-standard + * SQL Anywhere options. + * + * @param Index $index Index definition + * + * @return string + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + $sql = ''; + + if ( ! $index->isPrimary() && $index->hasFlag('for_olap_workload')) { + $sql .= ' FOR OLAP WORKLOAD'; + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')' + : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'; + } + + /** + * Returns the SQL snippet for creating a table constraint. + * + * @param Constraint $constraint The table constraint to create the SQL snippet for. + * @param string|null $name The table constraint name to use if any. + * + * @return string + * + * @throws \InvalidArgumentException if the given table constraint type is not supported by this method. + */ + protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null) + { + if ($constraint instanceof ForeignKeyConstraint) { + return $this->getForeignKeyDeclarationSQL($constraint); + } + + if ( ! $constraint instanceof Index) { + throw new \InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint)); + } + + if ( ! $constraint->isPrimary() && ! $constraint->isUnique()) { + throw new \InvalidArgumentException( + 'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' . + 'with getTableConstraintDeclarationSQL().' + ); + } + + $constraintColumns = $constraint->getQuotedColumns($this); + + if (empty($constraintColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + $sql = ''; + $flags = ''; + + if ( ! empty($name)) { + $name = new Identifier($name); + $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' '; + } + + if ($constraint->hasFlag('clustered')) { + $flags = 'CLUSTERED '; + } + + if ($constraint->isPrimary()) { + return $sql . 'PRIMARY KEY ' . $flags . '('. $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; + } + + return $sql . 'UNIQUE ' . $flags . '('. $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->hasFlag('virtual')) { + $type .= 'VIRTUAL '; + } + + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } + + return $type; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + 'ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this) + ); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords'; + } + + /** + * {@inheritdoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')'); + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'char' => 'string', + 'long nvarchar' => 'text', + 'long varchar' => 'text', + 'nchar' => 'string', + 'ntext' => 'text', + 'nvarchar' => 'string', + 'text' => 'text', + 'uniqueidentifierstr' => 'guid', + 'varchar' => 'string', + 'xml' => 'text', + 'bigint' => 'bigint', + 'unsigned bigint' => 'bigint', + 'bit' => 'boolean', + 'decimal' => 'decimal', + 'double' => 'float', + 'float' => 'float', + 'int' => 'integer', + 'integer' => 'integer', + 'unsigned int' => 'integer', + 'numeric' => 'decimal', + 'smallint' => 'smallint', + 'unsigned smallint', 'smallint', + 'tinyint' => 'smallint', + 'unsigned tinyint', 'smallint', + 'money' => 'decimal', + 'smallmoney' => 'decimal', + 'long varbit' => 'text', + 'varbit' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'smalldatetime' => 'datetime', + 'time' => 'time', + 'timestamp' => 'datetime', + 'binary' => 'binary', + 'image' => 'blob', + 'long binary' => 'blob', + 'uniqueidentifier' => 'guid', + 'varbinary' => 'binary', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php new file mode 100644 index 00000000000..71829cd0e0d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Table; + +/** + * Platform to ensure compatibility of Doctrine with SQL Azure + * + * On top of SQL Server 2008 the following functionality is added: + * + * - Create tables with the FEDERATED ON syntax. + */ +class SQLAzurePlatform extends SQLServer2008Platform +{ + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags=self::CREATE_INDEXES) + { + $sql = parent::getCreateTableSQL($table, $createFlags); + + if ($table->hasOption('azure.federatedOnColumnName')) { + $distributionName = $table->getOption('azure.federatedOnDistributionName'); + $columnName = $table->getOption('azure.federatedOnColumnName'); + $stmt = ' FEDERATED ON (' . $distributionName . ' = ' . $columnName . ')'; + + $sql[0] = $sql[0] . $stmt; + } + + return $sql; + } +} 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 00000000000..6dc1188fadf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2005 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 +{ + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2005 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords'; + } +} 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 00000000000..8b1184d195a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2008 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 +{ + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIMEOFFSET(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.u P'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * {@inheritDoc} + * + * Adding Datetime2 Type + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['datetime2'] = 'datetime'; + $this->doctrineTypeMapping['date'] = 'date'; + $this->doctrineTypeMapping['time'] = 'time'; + $this->doctrineTypeMapping['datetimeoffset'] = 'datetimetz'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2008 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php new file mode 100644 index 00000000000..fb78a80e047 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php @@ -0,0 +1,105 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Sequence; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2012 version. + * + * Differences to SQL Server 2008 and before are that sequences are introduced. + * + * @author Steve Müller + */ +class SQLServer2012Platform extends SQLServer2008Platform +{ + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT seq.name, + CAST( + seq.increment AS VARCHAR(MAX) + ) AS increment, -- CAST avoids driver error for sql_variant type + CAST( + seq.start_value AS VARCHAR(MAX) + ) AS start_value -- CAST avoids driver error for sql_variant type + FROM sys.sequences AS seq'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT NEXT VALUE FOR ' . $sequenceName; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2012 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords'; + } +} 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 00000000000..1a5f99ceb48 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -0,0 +1,1587 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use 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 + * @author Steve Müller + */ +class SQLServerPlatform extends AbstractPlatform +{ + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ('-' === $operator) { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server prefers "autoincrement" identity columns + * since sequences can only be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'dbo'; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } elseif (!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; + } + + if ($table instanceof 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; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $defaultConstraintsSql = array(); + $commentsSql = array(); + + // @todo does other code breaks because of this? + // force primary keys to be not null + foreach ($columns as &$column) { + if (isset($column['primary']) && $column['primary']) { + $column['notnull'] = true; + } + + // Build default constraints SQL statements. + if (isset($column['default'])) { + $defaultConstraintsSql[] = 'ALTER TABLE ' . $tableName . + ' ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $column); + } + + if ( ! empty($column['comment']) || is_numeric($column['comment'])) { + $commentsSql[] = $this->getCreateColumnCommentSQL($tableName, $column['name'], $column['comment']); + } + } + + $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'])) { + $flags = ''; + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . 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 array_merge($sql, $commentsSql, $defaultConstraintsSql); + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + $flags = ''; + if ($index->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY' . $flags . ' (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')'; + } + + /** + * Returns the SQL statement for creating a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to create the comment for. + * @param string $comment The column's comment. + * + * @return string + */ + protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) + { + return $this->getAddExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL snippet for declaring a default constraint. + * + * @param string $table Name of the table to return the default constraint declaration for. + * @param array $column Column definition. + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDefaultConstraintDeclarationSQL($table, array $column) + { + if ( ! isset($column['default'])) { + throw new \InvalidArgumentException("Incomplete column definition. 'default' required."); + } + + $columnName = new Identifier($column['name']); + + return + ' CONSTRAINT ' . + $this->generateDefaultConstraintName($table, $column['name']) . + $this->getDefaultValueDeclarationSQL($column) . + ' FOR ' . $columnName->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index); + + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + + return $constraint; + } + + /** + * {@inheritDoc} + */ + public function getCreateIndexSQL(Index $index, $table) + { + $constraint = parent::getCreateIndexSQL($index, $table); + + if ($index->isUnique() && !$index->isPrimary()) { + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + } + + return $constraint; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } elseif ($index->hasFlag('nonclustered')) { + $type .= 'NONCLUSTERED '; + } + + return $type; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + private function _appendUniqueConstraintDefinition($sql, Index $index) + { + $fields = array(); + + foreach ($index->getQuotedColumns($this) as $field) { + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = array(); + $sql = array(); + $columnSql = array(); + $commentsSql = array(); + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if (isset($columnDef['default'])) { + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + + $comment = $this->getColumnComment($column); + + if ( ! empty($comment) || is_numeric($comment)) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $comment = $this->getColumnComment($column); + $hasComment = ! empty ($comment) || is_numeric($comment); + + if ($columnDiff->fromColumn instanceof Column) { + $fromComment = $this->getColumnComment($columnDiff->fromColumn); + $hasFromComment = ! empty ($fromComment) || is_numeric($fromComment); + + if ($hasFromComment && $hasComment && $fromComment != $comment) { + $commentsSql[] = $this->getAlterColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } elseif ($hasFromComment && ! $hasComment) { + $commentsSql[] = $this->getDropColumnCommentSQL($diff->name, $column->getQuotedName($this)); + } elseif ($hasComment) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + } else { + // todo: Original comment cannot be determined. What to do? Add, update, drop or skip? + } + + // Do not add query part if only comment has changed. + if ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1) { + continue; + } + + $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); + + if ($requireDropDefaultConstraint) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $columnDiff->oldColumnName + ); + } + + $columnDef = $column->toArray(); + + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if (isset($columnDef['default']) && ($requireDropDefaultConstraint || $columnDiff->hasChanged('default'))) { + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = "sp_RENAME '" . + $diff->getName($this)->getQuotedName($this) . "." . $oldColumnName->getQuotedName($this) . + "', '" . $column->getQuotedName($this) . "', 'COLUMN'"; + + // Recreate default constraint with new column name if necessary (for future reference). + if ($column->getDefault() !== null) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $oldColumnName->getQuotedName($this) + ); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + } + + $tableSql = array(); + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + $sql = array_merge($sql, $commentsSql); + + if ($diff->newName !== false) { + $sql[] = "sp_RENAME '" . $diff->getName($this)->getQuotedName($this) . "', '" . $diff->getNewName()->getName() . "'"; + + /** + * Rename table's default constraints names + * to match the new table name. + * This is necessary to ensure that the default + * constraints can be referenced in future table + * alterations as the table name is encoded in + * default constraints' names. + */ + $sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " . + "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " . + "+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " . + "'" . $this->generateIdentifierName($diff->newName) . "') + ''', ''OBJECT'';' " . + "FROM sys.default_constraints dc " . + "JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " . + "WHERE tbl.name = '" . $diff->getNewName()->getName() . "';" . + "EXEC sp_executesql @sql"; + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param Column $column The column to generate the clause for. + * + * @return string + */ + private function getAlterTableAddDefaultConstraintClause($tableName, Column $column) + { + $columnDef = $column->toArray(); + $columnDef['name'] = $column->getQuotedName($this); + + return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); + } + + /** + * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param string $columnName The name of the column to generate the clause for. + * + * @return string + */ + private function getAlterTableDropDefaultConstraintClause($tableName, $columnName) + { + return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); + } + + /** + * Checks whether a column alteration requires dropping its default constraint first. + * + * Different to other database vendors SQL Server implements column default values + * as constraints and therefore changes in a column's default value as well as changes + * in a column's type require dropping the default constraint first before being to + * alter the particular column to the new definition. + * + * @param ColumnDiff $columnDiff The column diff to evaluate. + * + * @return boolean True if the column alteration requires dropping its default constraint first, false otherwise. + */ + private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff) + { + // We can only decide whether to drop an existing default constraint + // if we know the original default value. + if ( ! $columnDiff->fromColumn instanceof Column) { + return false; + } + + // We only need to drop an existing default constraint if we know the + // column was defined with a default value before. + if ($columnDiff->fromColumn->getDefault() === null) { + return false; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and it has changed. + if ($columnDiff->hasChanged('default')) { + return true; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and the native column type has changed. + if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('fixed')) { + return true; + } + + return false; + } + + /** + * Returns the SQL statement for altering a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to alter the comment for. + * @param string $comment The column's comment. + * + * @return string + */ + protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) + { + return $this->getUpdateExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL statement for dropping a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to drop the comment for. + * + * @return string + */ + protected function getDropColumnCommentSQL($tableName, $columnName) + { + return $this->getDropExtendedPropertySQL( + 'MS_Description', + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + sprintf( + "EXEC sp_RENAME N'%s.%s', N'%s', N'INDEX'", + $tableName, + $oldIndexName, + $index->getQuotedName($this) + ) + ); + } + + /** + * Returns the SQL statement for adding an extended property to a database object. + * + * @param string $name The name of the property to add. + * @param string|null $value The value of the property to add. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx + */ + public function getAddExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_addextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", N" . $this->quoteStringLiteral($value) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * Returns the SQL statement for dropping an extended property from a database object. + * + * @param string $name The name of the property to drop. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx + */ + public function getDropExtendedPropertySQL( + $name, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_dropextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * Returns the SQL statement for updating an extended property of a database object. + * + * @param string $name The name of the property to update. + * @param string|null $value The value of the property to update. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx + */ + public function getUpdateExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_updateextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", N" . $this->quoteStringLiteral($value) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication + return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT col.name, + type.name AS type, + col.max_length AS length, + ~col.is_nullable AS notnull, + def.definition AS [default], + col.scale, + col.precision, + col.is_identity AS autoincrement, + col.collation_name AS collation, + CAST(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type + FROM sys.columns AS col + JOIN sys.types AS type + ON col.user_type_id = type.user_type_id + JOIN sys.objects AS obj + ON col.object_id = obj.object_id + JOIN sys.schemas AS scm + ON obj.schema_id = scm.schema_id + LEFT JOIN sys.default_constraints def + ON col.default_object_id = def.object_id + AND col.object_id = def.parent_object_id + LEFT JOIN sys.extended_properties AS prop + ON obj.object_id = prop.major_id + AND col.column_id = prop.minor_id + AND prop.name = 'MS_Description' + WHERE obj.type = 'U' + AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); + } + + /** + * {@inheritDoc} + */ + 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 " . + $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)'); + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT idx.name AS key_name, + col.name AS column_name, + ~idx.is_unique AS non_unique, + idx.is_primary_key AS [primary], + CASE idx.type + WHEN '1' THEN 'clustered' + WHEN '2' THEN 'nonclustered' + ELSE NULL + END AS flags + FROM sys.tables AS tbl + JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id + JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id + JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id + JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id + WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . " + ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; + } + + /** + * Returns the where clause to filter schema and table name in a query. + * + * @param string $table The full qualified name of the table. + * @param string $schemaColumn The name of the column to compare the schema to in the where clause. + * @param string $tableColumn The name of the column to compare the table to in the where clause. + * + * @return string + */ + private function getTableWhereClause($table, $schemaColumn, $tableColumn) + { + if (strpos($table, ".") !== false) { + list($schema, $table) = explode(".", $table); + $schema = "'" . $schema . "'"; + } else { + $schema = "SCHEMA_NAME()"; + } + + return "({$tableColumn} = '{$table}' AND {$schemaColumn} = {$schema})"; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'NEWID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'CHARINDEX(' . $substr . ', ' . $str . ')'; + } + + return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + if ( ! $char) { + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + return 'LTRIM(RTRIM(' . $str . '))'; + } + + return $trimFn . '(' . $str . ')'; + } + + /** 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)'; + } + + if ($pos == self::TRIM_TRAILING) { + return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; + } + + 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))'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return '(' . implode(' + ', $args) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT * FROM sys.databases'; + } + + /** + * {@inheritDoc} + */ + public function getListNamespacesSQL() + { + return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if (!is_null($length)) { + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)'; + } + + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LEN(' . $column . ')'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 8000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return (!empty($columnDef['autoincrement'])) ? ' IDENTITY' : ''; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BIT'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null) { + return $query; + } + + $start = $offset + 1; + $end = $offset + $limit; + + // We'll find a SELECT or SELECT distinct and prepend TOP n to it + // Even if the TOP n is very large, the use of a CTE will + // allow the SQL Server query planner to optimize it so it doesn't + // actually scan the entire range covered by the TOP clause. + $selectPattern = '/^(\s*SELECT\s+(?:DISTINCT\s+)?)(.*)$/i'; + $replacePattern = sprintf('$1%s $2', "TOP $end"); + $query = preg_replace($selectPattern, $replacePattern, $query); + + if (stristr($query, "ORDER BY")) { + // Inner order by is not valid in SQL Server for our purposes + // unless it's in a TOP N subquery. + $query = $this->scrubInnerOrderBy($query); + } + + // Build a new limited query around the original, using a CTE + return sprintf( + "WITH dctrn_cte AS (%s) " + . "SELECT * FROM (" + . "SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte" + . ") AS doctrine_tbl " + . "WHERE doctrine_rownum BETWEEN %d AND %d ORDER BY doctrine_rownum ASC", + $query, + $start, + $end + ); + } + + /** + * Remove ORDER BY clauses in subqueries - they're not supported by SQL Server. + * Caveat: will leave ORDER BY in TOP N subqueries. + * + * @param $query + * @return string + */ + private function scrubInnerOrderBy($query) + { + $count = substr_count(strtoupper($query), "ORDER BY"); + $offset = 0; + + while ($count-- > 0) { + $qLen = strlen($query); + $orderByPos = stripos($query, " ORDER BY", $offset); + $parenCount = 0; + $currentPosition = $orderByPos; + + while ($parenCount >= 0 && $currentPosition < $qLen) { + if ($query[$currentPosition] === '(') { + $parenCount++; + } elseif ($query[$currentPosition] === ')') { + $parenCount--; + } + + $currentPosition++; + } + + if ($this->isOrderByInTopNSubquery($query, $orderByPos)) { + // If the order by clause is in a TOP N subquery, do not remove + // it and continue iteration from the current position. + $offset = $currentPosition; + continue; + } + + if ($currentPosition < $qLen - 1) { + $query = substr($query, 0, $orderByPos) . substr($query, $currentPosition - 1); + $offset = $orderByPos; + } + } + return $query; + } + + /** + * Check an ORDER BY clause to see if it is in a TOP N query or subquery. + * + * @param string $query The query + * @param int $currentPosition Start position of ORDER BY clause + * @return bool true if ORDER BY is in a TOP N query, false otherwise + */ + private function isOrderByInTopNSubquery($query, $currentPosition) + { + // Grab query text on the same nesting level as the ORDER BY clause we're examining. + $subQueryBuffer = ''; + $parenCount = 0; + + // If $parenCount goes negative, we've exited the subquery we're examining. + // If $currentPosition goes negative, we've reached the beginning of the query. + while ($parenCount >= 0 && $currentPosition >= 0) { + if ($query[$currentPosition] === '(') { + $parenCount--; + } elseif ($query[$currentPosition] === ')') { + $parenCount++; + } + + // Only yank query text on the same nesting level as the ORDER BY clause. + $subQueryBuffer = ($parenCount === 0 ? $query[$currentPosition] : " ") . $subQueryBuffer; + + $currentPosition--; + } + + if (preg_match('/SELECT\s+(DISTINCT\s+)?TOP\s/i', $subQueryBuffer)) { + return true; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return false; + } + + /** + * {@inheritDoc} + */ + 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; + } + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 1 : 0; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return '#' . $tableName; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mssql'; + } + + /** + * {@inheritDoc} + */ + 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', + 'smalldatetime' => 'datetime', + 'datetime' => 'datetime', + 'char' => 'string', + 'varchar' => 'string', + 'text' => 'text', + 'nchar' => 'string', + 'nvarchar' => 'string', + 'ntext' => 'text', + 'binary' => 'binary', + 'varbinary' => 'binary', + 'image' => 'blob', + 'uniqueidentifier' => 'guid', + ); + } + + /** + * {@inheritDoc} + */ + public function createSavePoint($savepoint) + { + return 'SAVE TRANSACTION ' . $savepoint; + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TRANSACTION ' . $savepoint; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + // RESTRICT is not supported, therefore falling back to NO ACTION. + if (strtoupper($action) === 'RESTRICT') { + return 'NO ACTION'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + /** + * {@inheritDoc} + */ + public function appendLockHint($fromClause, $lockMode) + { + switch (true) { + case LockMode::NONE === $lockMode: + return $fromClause . ' WITH (NOLOCK)'; + + case LockMode::PESSIMISTIC_READ === $lockMode: + return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; + + case LockMode::PESSIMISTIC_WRITE === $lockMode: + return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; + + default: + return $fromClause; + } + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' '; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords'; + } + + /** + * {@inheritDoc} + */ + public function quoteSingleIdentifier($str) + { + return "[" . str_replace("]", "][", $str) . "]"; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'VARBINARY(MAX)'; + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! isset($field['default'])) { + return empty($field['notnull']) ? ' NULL' : ''; + } + + if ( ! isset($field['type'])) { + return " DEFAULT '" . $field['default'] . "'"; + } + + if (in_array((string) $field['type'], array('Integer', 'BigInt', 'SmallInt'))) { + return " DEFAULT " . $field['default']; + } + + if (in_array((string) $field['type'], array('DateTime', 'DateTimeTz')) && $field['default'] == $this->getCurrentTimestampSQL()) { + return " DEFAULT " . $this->getCurrentTimestampSQL(); + } + + if ((string) $field['type'] == 'Boolean') { + return " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + + return " DEFAULT '" . $field['default'] . "'"; + } + + /** + * {@inheritdoc} + * + * Modifies column declaration order as it differs in Microsoft SQL Server. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $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 . $collation . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + /** + * Returns a unique default constraint name for a table and column. + * + * @param string $table Name of the table to generate the unique default constraint name for. + * @param string $column Name of the column in the table to generate the unique default constraint name for. + * + * @return string + */ + private function generateDefaultConstraintName($table, $column) + { + return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); + } + + /** + * Returns a hash value for a given identifier. + * + * @param string $identifier Identifier to generate a hash value for. + * + * @return string + */ + private function generateIdentifierName($identifier) + { + // Always generate name for unquoted identifiers to ensure consistency. + $identifier = new Identifier($identifier); + + return strtoupper(dechex(crc32($identifier->getName()))); + } +} 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 00000000000..3d8a09b63ee --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -0,0 +1,1113 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Constraint; + +/** + * The SqlitePlatform class describes the specifics and dialects of the SQLite + * database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Martin Hasoň + * @todo Rename: SQLitePlatform + */ +class SqlitePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'REGEXP'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || " + . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))"; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'time': + return 'time(\'now\')'; + case 'date': + return 'date(\'now\')'; + case 'timestamp': + default: + return 'datetime(\'now\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $trimChar = ($char != false) ? (', ' . $char) : ''; + + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + $trimFn = 'TRIM'; + } + + return $trimFn . '(' . $str . $trimChar . ')'; + } + + /** + * {@inheritDoc} + * + * SQLite only supports the 2 parameter variant of this function + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE('.$str.', '.$substr.')'; + } + + return 'LOCATE('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_SECOND: + case self::DATE_INTERVAL_UNIT_MINUTE: + case self::DATE_INTERVAL_UNIT_HOUR: + return "DATETIME(" . $date . ",'" . $operator . $interval . " " . $unit . "')"; + + default: + switch ($unit) { + case self::DATE_INTERVAL_UNIT_WEEK: + $interval *= 7; + $unit = self::DATE_INTERVAL_UNIT_DAY; + break; + + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + break; + } + + return "DATE(" . $date . ",'" . $operator . $interval . " " . $unit . "')"; + } + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))'; + } + + /** + * {@inheritDoc} + */ + 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); + } + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getTinyIntTypeDeclarationSql(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getMediumIntTypeDeclarationSql(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + // sqlite autoincrement is implicit for integer PKs, but not when the field is unsigned + if ( ! empty($columnDef['autoincrement'])) { + return ''; + } + + return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; + } + + /** + * {@inheritDoc} + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( + $foreignKey->getQuotedLocalColumns($this), + str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)), + $foreignKey->getQuotedForeignColumns($this), + $foreignKey->getName(), + $foreignKey->getOptions() + )); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = array()) + { + $name = str_replace('.', '__', $name); + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; + } + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $foreignKey) { + $queryFields.= ', '.$this->getForeignKeyDeclarationSQL($foreignKey); + } + } + + $query[] = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + + if (isset($options['alter']) && true === $options['alter']) { + return $query; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + if (isset($options['unique']) && ! empty($options['unique'])) { + foreach ($options['unique'] as $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'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BLOB'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 0; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + /** + * {@inheritDoc} + */ + 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"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA table_info('$table')"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA index_list('$table')"; + } + + /** + * {@inheritDoc} + */ + 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"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + $query .= (($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) ? ' ' : ' NOT ') . 'DEFERRABLE'; + $query .= ' INITIALLY ' . (($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ? 'DEFERRED' : 'IMMEDIATE'); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + 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 integer|float $value + * + * @return float + */ + static public function udfSqrt($value) + { + return sqrt($value); + } + + /** + * User-defined function for Sqlite that implements MOD(a, b). + * + * @param integer $a + * @param integer $b + * + * @return integer + */ + static public function udfMod($a, $b) + { + return ($a % $b); + } + + /** + * @param string $str + * @param string $substr + * @param integer $offset + * + * @return integer + */ + static public function udfLocate($str, $substr, $offset = 0) + { + // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions. + // So we have to make them compatible if an offset is given. + if ($offset > 0) { + $offset -= 1; + } + + $pos = strpos($str, $substr, $offset); + + if ($pos !== false) { + return $pos + 1; + } + + return 0; + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSql() + { + return ''; + } + + /** + * {@inheritDoc} + */ + 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', + ); + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords'; + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + foreach ($diff->fromTable->getIndexes() as $index) { + if ( ! $index->isPrimary()) { + $sql[] = $this->getDropIndexSQL($index, $diff->name); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + $tableName = $diff->newName ? $diff->getNewName(): $diff->getName($this); + foreach ($this->getIndexesInAlteredTable($diff) as $index) { + if ($index->isPrimary()) { + continue; + } + + $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this)); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if (null === $limit && null !== $offset) { + return $query . ' LIMIT -1 OFFSET ' . $offset; + } + + return parent::doModifyLimitQuery($query, $limit, $offset); + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + $tableName = str_replace('.', '__', $tableName); + + return $tableName; + } + + /** + * {@inheritDoc} + * + * 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. + */ + public function canEmulateSchemas() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyConstraints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + throw new DBALException('Sqlite platform does not support alter primary key.'); + } + + /** + * {@inheritdoc} + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritdoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + throw new DBALException('Sqlite platform does not support alter constraint.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags = null) + { + $createFlags = null === $createFlags ? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS : $createFlags; + + return parent::getCreateTableSQL($table, $createFlags); + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA foreign_key_list('$table')"; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = $this->getSimpleAlterTableSQL($diff); + if (false !== $sql) { + return $sql; + } + + $fromTable = $diff->fromTable; + if ( ! $fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $table = clone $fromTable; + + $columns = array(); + $oldColumnNames = array(); + $newColumnNames = array(); + $columnSql = array(); + + foreach ($table->getColumns() as $columnName => $column) { + $columnName = strtolower($columnName); + $columns[$columnName] = $column; + $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); + } + + foreach ($diff->removedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + unset($oldColumnNames[$columnName]); + unset($newColumnNames[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = strtolower($oldColumnName); + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($column->getName())] = $column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $column->getQuotedName($this); + } + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this); + } + } + + foreach ($diff->addedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columns[strtolower($columnName)] = $column; + } + + $sql = array(); + $tableSql = array(); + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $dataTable = new Table('__temp__'.$table->getName()); + + $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions()); + $newTable->addOption('alter', true); + + $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); + //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0)); + $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($fromTable); + + $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); + $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($dataTable); + + if ($diff->newName && $diff->newName != $diff->name) { + $renamedTable = $diff->getNewName(); + $sql[] = 'ALTER TABLE '.$newTable->getQuotedName($this).' RENAME TO '.$renamedTable->getQuotedName($this); + } + + $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array|bool + */ + private function getSimpleAlterTableSQL(TableDiff $diff) + { + // Suppress changes on integer type autoincrement columns. + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + if ( ! $columnDiff->fromColumn instanceof Column || + ! $columnDiff->column instanceof Column || + ! $columnDiff->column->getAutoincrement() || + ! (string) $columnDiff->column->getType() === 'Integer' + ) { + continue; + } + + if ( ! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) { + unset($diff->changedColumns[$oldColumnName]); + + continue; + } + + $fromColumnType = (string) $columnDiff->fromColumn->getType(); + + if ($fromColumnType === 'SmallInt' || $fromColumnType === 'BigInt') { + unset($diff->changedColumns[$oldColumnName]); + } + } + + if ( ! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes) + || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes) + || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes) + || ! empty($diff->renamedIndexes) + ) { + return false; + } + + $table = new Table($diff->name); + + $sql = array(); + $tableSql = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $field = array_merge(array('unique' => null, 'autoincrement' => null, 'default' => null), $column->toArray()); + $type = (string) $field['type']; + switch (true) { + case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']: + case $type == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL(): + case $type == 'Date' && $field['default'] == $this->getCurrentDateSQL(): + case $type == 'Time' && $field['default'] == $this->getCurrentTimeSQL(): + return false; + } + + $field['name'] = $column->getQuotedName($this); + if (strtolower($field['type']) == 'string' && $field['length'] === null) { + $field['length'] = 255; + } + + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' ADD COLUMN '.$this->getColumnDeclarationSQL($field['name'], $field); + } + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $newTable = new Identifier($diff->newName); + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' RENAME TO '.$newTable->getQuotedName($this); + } + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getColumnNamesInAlteredTable(TableDiff $diff) + { + $columns = array(); + + foreach ($diff->fromTable->getColumns() as $columnName => $column) { + $columns[strtolower($columnName)] = $column->getName(); + } + + foreach ($diff->removedColumns as $columnName => $column) { + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + $columnName = $column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + $columnName = $columnDiff->column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->addedColumns as $columnName => $column) { + $columns[strtolower($columnName)] = $columnName; + } + + return $columns; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return \Doctrine\DBAL\Schema\Index[] + */ + private function getIndexesInAlteredTable(TableDiff $diff) + { + $indexes = $diff->fromTable->getIndexes(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($indexes as $key => $index) { + foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) { + if (strtolower($key) === strtolower($oldIndexName)) { + unset($indexes[$key]); + } + } + + $changed = false; + $indexColumns = array(); + foreach ($index->getColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($indexes[$key]); + continue 2; + } else { + $indexColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags()); + } + } + + foreach ($diff->removedIndexes as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName) && isset($indexes[$indexName])) { + unset($indexes[$indexName]); + } + } + + foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName)) { + $indexes[$indexName] = $index; + } else { + $indexes[] = $index; + } + } + + return $indexes; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getForeignKeysInAlteredTable(TableDiff $diff) + { + $foreignKeys = $diff->fromTable->getForeignKeys(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($foreignKeys as $key => $constraint) { + $changed = false; + $localColumns = array(); + foreach ($constraint->getLocalColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($foreignKeys[$key]); + continue 2; + } else { + $localColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions()); + } + } + + foreach ($diff->removedForeignKeys as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName) && isset($foreignKeys[$constraintName])) { + unset($foreignKeys[$constraintName]); + } + } + + foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName)) { + $foreignKeys[$constraintName] = $constraint; + } else { + $foreignKeys[] = $constraint; + } + } + + return $foreignKeys; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getPrimaryIndexInAlteredTable(TableDiff $diff) + { + $primaryIndex = array(); + + foreach ($this->getIndexesInAlteredTable($diff) as $index) { + if ($index->isPrimary()) { + $primaryIndex = array($index->getName() => $index); + } + } + + return $primaryIndex; + } +} 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 00000000000..df6b957a8dd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php @@ -0,0 +1,150 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use Doctrine\DBAL\Cache\QueryCacheProfile; + +/** + * Portability wrapper for a Connection. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +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_DB2 = 13; + const PORTABILITY_ORACLE = 9; + const PORTABILITY_POSTGRESQL = 13; + const PORTABILITY_SQLITE = 13; + const PORTABILITY_OTHERVENDORS = 12; + const PORTABILITY_DRIZZLE = 13; + const PORTABILITY_SQLANYWHERE = 13; + const PORTABILITY_SQLSRV = 13; + + /** + * @var integer + */ + private $portability = self::PORTABILITY_NONE; + + /** + * @var integer + */ + private $case; + + /** + * {@inheritdoc} + */ + public function connect() + { + $ret = parent::connect(); + if ($ret) { + $params = $this->getParams(); + if (isset($params['portability'])) { + if ($this->getDatabasePlatform()->getName() === "oracle") { + $params['portability'] = $params['portability'] & self::PORTABILITY_ORACLE; + } elseif ($this->getDatabasePlatform()->getName() === "postgresql") { + $params['portability'] = $params['portability'] & self::PORTABILITY_POSTGRESQL; + } elseif ($this->getDatabasePlatform()->getName() === "sqlite") { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLITE; + } elseif ($this->getDatabasePlatform()->getName() === "drizzle") { + $params['portability'] = self::PORTABILITY_DRIZZLE; + } elseif ($this->getDatabasePlatform()->getName() === 'sqlanywhere') { + $params['portability'] = self::PORTABILITY_SQLANYWHERE; + } elseif ($this->getDatabasePlatform()->getName() === 'db2') { + $params['portability'] = self::PORTABILITY_DB2; + } elseif ($this->getDatabasePlatform()->getName() === 'mssql') { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLSRV; + } 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; + } + + /** + * @return integer + */ + public function getPortability() + { + return $this->portability; + } + + /** + * @return integer + */ + public function getFetchCase() + { + return $this->case; + } + + /** + * {@inheritdoc} + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + $stmt = new Statement(parent::executeQuery($query, $params, $types, $qcp), $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function prepare($statement) + { + $stmt = new Statement(parent::prepare($statement), $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function query() + { + $this->connect(); + + $stmt = call_user_func_array(array($this->_conn, 'query'), func_get_args()); + $stmt = new Statement($stmt, $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } +} 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 00000000000..352fda9d762 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php @@ -0,0 +1,240 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use PDO; + +/** + * Portability wrapper for a Statement. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement +{ + /** + * @var integer + */ + private $portability; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $stmt; + + /** + * @var integer + */ + private $case; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * Wraps Statement and applies portability measures. + * + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\DBAL\Portability\Connection $conn + */ + public function __construct($stmt, Connection $conn) + { + $this->stmt = $stmt; + $this->portability = $conn->getPortability(); + $this->case = $conn->getFetchCase(); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + return $this->stmt->bindParam($column, $variable, $type, $length); + } + /** + * {@inheritdoc} + */ + + public function bindValue($param, $value, $type = null) + { + return $this->stmt->bindValue($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + return $this->stmt->execute($params); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg1 = null, $arg2 = null) + { + $this->defaultFetchMode = $fetchMode; + + return $this->stmt->setFetchMode($fetchMode, $arg1, $arg2); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + $row = $this->stmt->fetch($fetchMode); + + $row = $this->fixRow($row, + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM), + !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE) + ); + + return $row; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $columnIndex = 0) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($columnIndex != 0) { + $rows = $this->stmt->fetchAll($fetchMode, $columnIndex); + } else { + $rows = $this->stmt->fetchAll($fetchMode); + } + + $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM); + $fixCase = !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == 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; + } + + /** + * @param mixed $row + * @param integer $iterateRow + * @param boolean $fixCase + * + * @return array + */ + 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; + } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($v)) { + $row[$k] = rtrim($v); + } + } + } + + return $row; + } + + /** + * {@inheritdoc} + */ + 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; + } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($value)) { + $value = rtrim($value); + } + } + + return $value; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..937726f044f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +/** + * Composite expression is responsible to build a group of similar expression. + * + * @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'; + + /** + * The instance type of composite expression. + * + * @var string + */ + private $type; + + /** + * Each expression part of the composite expression. + * + * @var array + */ + 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 $parts + * + * @return \Doctrine\DBAL\Query\Expression\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 \Doctrine\DBAL\Query\Expression\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); + } + + /** + * Retrieves 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) . ')'; + } + + /** + * Returns the type of this composite expression (AND/OR). + * + * @return string + */ + public function getType() + { + return $this->type; + } +} 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 00000000000..2736fcf2264 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -0,0 +1,313 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +use Doctrine\DBAL\Connection; + +/** + * ExpressionBuilder class is responsible to dynamically create SQL query parts. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class ExpressionBuilder +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * The DBAL Connection. + * + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * Initializes a new ExpressionBuilder. + * + * @param \Doctrine\DBAL\Connection $connection The 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 \Doctrine\DBAL\Query\Expression\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 \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * Creates a comparison expression. + * + * @param mixed $x The left expression. + * @param string $operator One of the ExpressionBuilder::* constants. + * @param mixed $y The 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 The left expression. + * @param mixed $y The 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 The left expression. + * @param mixed $y The 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 The left expression. + * @param mixed $y The 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 The left expression. + * @param mixed $y The 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 The left expression. + * @param mixed $y The 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 The left expression. + * @param mixed $y The 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 The 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 The 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); + } + + /** + * Creates a NOT LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by NOT LIKE() comparison. + * @param mixed $y Argument to be used in NOT LIKE() comparison. + * + * @return string + */ + public function notLike($x, $y) + { + return $this->comparison($x, 'NOT LIKE', $y); + } + + /** + * Creates a IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by IN() comparison. + * @param string|array $y The placeholder or the array of values to be used by IN() comparison. + * + * @return string + */ + public function in($x, $y) + { + return $this->comparison($x, 'IN', '('.implode(', ', (array) $y).')'); + } + + /** + * Creates a NOT IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by NOT IN() comparison. + * @param string|array $y The placeholder or the array of values to be used by NOT IN() comparison. + * + * @return string + */ + public function notIn($x, $y) + { + return $this->comparison($x, 'NOT IN', '('.implode(', ', (array) $y).')'); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input The parameter to be quoted. + * @param string|null $type The type of the parameter. + * + * @return string + */ + public function literal($input, $type = null) + { + return $this->connection->quote($input, $type); + } +} 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 00000000000..0a6d59e3745 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -0,0 +1,1354 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\Query\Expression\CompositeExpression; +use 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. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class QueryBuilder +{ + /* + * The query types. + */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + const INSERT = 3; + + /* + * The builder states. + */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * The DBAL Connection. + * + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * @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(), + 'values' => array(), + ); + + /** + * The complete SQL string for this query. + * + * @var string + */ + private $sql; + + /** + * The query parameters. + * + * @var array + */ + private $params = array(); + + /** + * The parameter type map of this query. + * + * @var array + */ + private $paramTypes = array(); + + /** + * The type of query this is. Can be select, update or delete. + * + * @var integer + */ + private $type = self::SELECT; + + /** + * The state of the query object. Can be dirty or clean. + * + * @var integer + */ + private $state = self::STATE_CLEAN; + + /** + * The index of the first result to retrieve. + * + * @var integer + */ + private $firstResult = null; + + /** + * The maximum number of results to retrieve. + * + * @var integer + */ + private $maxResults = null; + + /** + * The counter of bound parameters used with {@see bindValue). + * + * @var integer + */ + private $boundCounter = 0; + + /** + * Initializes a new QueryBuilder. + * + * @param \Doctrine\DBAL\Connection $connection The 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\Expression\ExpressionBuilder + */ + public function expr() + { + return $this->connection->getExpressionBuilder(); + } + + /** + * Gets the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->type; + } + + /** + * Gets the associated DBAL Connection for this query builder. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Gets the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->state; + } + + /** + * Executes 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 \Doctrine\DBAL\Driver\Statement|int + */ + 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); + } + } + + /** + * Gets 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; + } + + switch ($this->type) { + case self::INSERT: + $sql = $this->getSQLForInsert(); + break; + 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 One of the PDO::PARAM_* constants. + * + * @return \Doctrine\DBAL\Query\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. + * @param array $types The query parameters types to set. + * + * @return \Doctrine\DBAL\Query\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 indexed by parameter index or name. + * + * @return array The currently defined query parameters indexed by parameter index or name. + */ + 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; + } + + /** + * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. + * + * @return array The currently defined query parameter types indexed by parameter index or name. + */ + public function getParameterTypes() + { + return $this->paramTypes; + } + + /** + * Gets a (previously set) query parameter type of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter type. + * + * @return mixed The value of the bound parameter type. + */ + public function getParameterType($key) + { + return isset($this->paramTypes[$key]) ? $this->paramTypes[$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 The 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 boolean $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; + } + } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) { + $key = key($sqlPart); + $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; + } elseif ($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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 + )); + } + + /** + * Turns the query being built into an insert query that inserts into + * a certain table + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param string $insert The table into which the rows should be inserted. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function insert($insert = null) + { + $this->type = self::INSERT; + + if ( ! $insert) { + return $this; + } + + return $this->add('from', array( + 'table' => $insert + )); + } + + /** + * Creates and adds 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|null $alias The alias of the table. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias = null) + { + 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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function andWhere($where) + { + $args = func_get_args(); + $where = $this->getQueryPart('where'); + + 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 \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function orWhere($where) + { + $args = func_get_args(); + $where = $this->getQueryPart('where'); + + 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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\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); + } + + /** + * Sets a value for a column in an insert query. + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?' + * ) + * ) + * ->setValue('password', '?'); + * + * + * @param string $column The column into which the value should be inserted. + * @param string $value The value that should be inserted into the column. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setValue($column, $value) + { + $this->sqlParts['values'][$column] = $value; + + return $this; + } + + /** + * Specifies values for an insert query indexed by column names. + * Replaces any previous values, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param array $values The values to specify for the insert query indexed by column names. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function values(array $values) + { + return $this->add('values', $values); + } + + /** + * 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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $args = func_get_args(); + $having = $this->getQueryPart('having'); + + 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 \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $args = func_get_args(); + $having = $this->getQueryPart('having'); + + 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 \Doctrine\DBAL\Query\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 \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true); + } + + /** + * Gets a query part by its name. + * + * @param string $queryPartName + * + * @return mixed + */ + public function getQueryPart($queryPartName) + { + return $this->sqlParts[$queryPartName]; + } + + /** + * Gets all query parts. + * + * @return array + */ + public function getQueryParts() + { + return $this->sqlParts; + } + + /** + * Resets SQL parts. + * + * @param array|null $queryPartNames + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function resetQueryParts($queryPartNames = null) + { + if (is_null($queryPartNames)) { + $queryPartNames = array_keys($this->sqlParts); + } + + foreach ($queryPartNames as $queryPartName) { + $this->resetQueryPart($queryPartName); + } + + return $this; + } + + /** + * Resets a single SQL part. + * + * @param string $queryPartName + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function resetQueryPart($queryPartName) + { + $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName]) + ? array() : null; + + $this->state = self::STATE_DIRTY; + + return $this; + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\Query\QueryException + */ + private function getSQLForSelect() + { + $query = 'SELECT ' . implode(', ', $this->sqlParts['select']) . ' FROM '; + + $query .= implode(', ', $this->getFromClauses()) + . ($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']) : ''); + + if ($this->isLimitQuery()) { + return $this->connection->getDatabasePlatform()->modifyLimitQuery( + $query, + $this->maxResults, + $this->firstResult + ); + } + + return $query; + } + + /** + * @return string[] + */ + private function getFromClauses() + { + $fromClauses = array(); + $knownAliases = array(); + + // Loop through all FROM clauses + foreach ($this->sqlParts['from'] as $from) { + if ($from['alias'] === null) { + $tableSql = $from['table']; + $tableReference = $from['table']; + } else { + $tableSql = $from['table'] . ' ' . $from['alias']; + $tableReference = $from['alias']; + } + + $knownAliases[$tableReference] = true; + + $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases); + } + + $this->verifyAllAliasesAreKnown($knownAliases); + + return $fromClauses; + } + + /** + * @param array $knownAliases + * + * @throws QueryException + */ + private function verifyAllAliasesAreKnown(array $knownAliases) + { + foreach ($this->sqlParts['join'] as $fromAlias => $joins) { + if ( ! isset($knownAliases[$fromAlias])) { + throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); + } + } + } + + /** + * @return bool + */ + private function isLimitQuery() + { + return $this->maxResults !== null || $this->firstResult !== null; + } + + /** + * Converts this instance into an INSERT string in SQL. + * + * @return string + */ + private function getSQLForInsert() + { + return 'INSERT INTO ' . $this->sqlParts['from']['table'] . + ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' . + ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')'; + } + + /** + * 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(); + } + + /** + * Creates 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; + } + + /** + * Creates 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 integer $type + * + * @return string + */ + public function createPositionalParameter($value, $type = \PDO::PARAM_STR) + { + $this->boundCounter++; + $this->setParameter($this->boundCounter, $value, $type); + + return "?"; + } + + /** + * @param string $fromAlias + * @param array $knownAliases + * + * @return string + */ + private function getSQLForJoins($fromAlias, array &$knownAliases) + { + $sql = ''; + + if (isset($this->sqlParts['join'][$fromAlias])) { + foreach ($this->sqlParts['join'][$fromAlias] as $join) { + if (array_key_exists($join['joinAlias'], $knownAliases)) { + throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases)); + } + $sql .= ' ' . strtoupper($join['joinType']) + . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] + . ' ON ' . ((string) $join['joinCondition']); + $knownAliases[$join['joinAlias']] = true; + } + + foreach ($this->sqlParts['join'][$fromAlias] as $join) { + $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases); + } + } + + return $sql; + } + + /** + * Deep clone of all expression objects in the SQL parts. + * + * @return void + */ + public function __clone() + { + foreach ($this->sqlParts as $part => $elements) { + if (is_array($this->sqlParts[$part])) { + foreach ($this->sqlParts[$part] as $idx => $element) { + if (is_object($element)) { + $this->sqlParts[$part][$idx] = clone $element; + } + } + } elseif (is_object($elements)) { + $this->sqlParts[$part] = clone $elements; + } + } + + foreach ($this->params as $name => $param) { + if (is_object($param)) { + $this->params[$name] = clone $param; + } + } + } +} 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 00000000000..92c9336d6e8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\DBALException; + +/** + * @since 2.1.4 + */ +class QueryException extends DBALException +{ + /** + * @param string $alias + * @param array $registeredAliases + * + * @return \Doctrine\DBAL\Query\QueryException + */ + static public function unknownAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not part of " . + "any FROM or JOIN clause table. The currently registered " . + "aliases are: " . implode(", ", $registeredAliases) . "."); + } + + /** + * @param string $alias + * @param array $registeredAliases + * + * @return \Doctrine\DBAL\Query\QueryException + */ + static public function nonUniqueAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not unique " . + "in FROM and JOIN clause table. The currently registered " . + "aliases are: " . implode(", ", $registeredAliases) . "."); + } +} 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 00000000000..e69de29bb2d 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 00000000000..a9037ffc5fe --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Utility class that parses sql statements with regard to types and parameters. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLParserUtils +{ + const POSITIONAL_TOKEN = '\?'; + const NAMED_TOKEN = '(? integer pair (indexed from zero) for a positional statement + * and a string => int[] pair for a named statement. + * + * @param string $statement + * @param boolean $isPositional + * + * @return array + */ + static public function getPlaceholderPositions($statement, $isPositional = true) + { + $match = ($isPositional) ? '?' : ':'; + if (strpos($statement, $match) === false) { + return array(); + } + + $token = ($isPositional) ? self::POSITIONAL_TOKEN : self::NAMED_TOKEN; + $paramMap = array(); + + foreach (self::getUnquotedStatementFragments($statement) as $fragment) { + preg_match_all("/$token/", $fragment[0], $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $placeholder) { + if ($isPositional) { + $paramMap[] = $placeholder[1] + $fragment[1]; + } else { + $pos = $placeholder[1] + $fragment[1]; + $paramMap[$pos] = substr($placeholder[0], 1, strlen($placeholder[0])); + } + } + } + + return $paramMap; + } + + /** + * For a positional query this method can rewrite the sql statement with regard to array parameters. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query. + * @param array $types The types the previous parameters are in. + * + * @return array + * + * @throws SQLParserUtilsException + */ + static public function expandListParameters($query, $params, $types) + { + $isPositional = is_int(key($params)); + $arrayPositions = array(); + $bindIndex = -1; + + if ($isPositional) { + ksort($params); + ksort($types); + } + + foreach ($types as $name => $type) { + ++$bindIndex; + + if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) { + continue; + } + + if ($isPositional) { + $name = $bindIndex; + } + + $arrayPositions[$name] = false; + } + + if (( ! $arrayPositions && $isPositional)) { + return array($query, $params, $types); + } + + $paramPos = self::getPlaceholderPositions($query, $isPositional); + + if ($isPositional) { + $paramOffset = 0; + $queryOffset = 0; + $params = array_values($params); + $types = array_values($types); + + foreach ($paramPos as $needle => $needlePos) { + if ( ! isset($arrayPositions[$needle])) { + continue; + } + + $needle += $paramOffset; + $needlePos += $queryOffset; + $count = 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), + $count ? + array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) : // array needles are at PDO::PARAM_* + 100 + array(), + array_slice($types, $needle + 1) + ); + + $expandStr = $count ? implode(", ", array_fill(0, $count, "?")) : 'NULL'; + $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); + + $paramOffset += ($count - 1); // Grows larger by number of parameters minus the replaced needle. + $queryOffset += (strlen($expandStr) - 1); + } + + return array($query, $params, $types); + } + + $queryOffset = 0; + $typesOrd = array(); + $paramsOrd = array(); + + foreach ($paramPos as $pos => $paramName) { + $paramLen = strlen($paramName) + 1; + $value = static::extractParam($paramName, $params, true); + + if ( ! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) { + $pos += $queryOffset; + $queryOffset -= ($paramLen - 1); + $paramsOrd[] = $value; + $typesOrd[] = static::extractParam($paramName, $types, false, \PDO::PARAM_STR); + $query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen)); + + continue; + } + + $count = count($value); + $expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; + + foreach ($value as $val) { + $paramsOrd[] = $val; + $typesOrd[] = static::extractParam($paramName, $types, false) - Connection::ARRAY_PARAM_OFFSET; + } + + $pos += $queryOffset; + $queryOffset += (strlen($expandStr) - $paramLen); + $query = substr($query, 0, $pos) . $expandStr . substr($query, ($pos + $paramLen)); + } + + return array($query, $paramsOrd, $typesOrd); + } + + /** + * Slice the SQL statement around pairs of quotes and + * return string fragments of SQL outside of quoted literals. + * Each fragment is captured as a 2-element array: + * + * 0 => matched fragment string, + * 1 => offset of fragment in $statement + * + * @param string $statement + * @return array + */ + static private function getUnquotedStatementFragments($statement) + { + $literal = self::ESCAPED_SINGLE_QUOTED_TEXT . '|' . + self::ESCAPED_DOUBLE_QUOTED_TEXT . '|' . + self::ESCAPED_BACKTICK_QUOTED_TEXT . '|' . + self::ESCAPED_BRACKET_QUOTED_TEXT; + preg_match_all("/([^'\"`\[]+)(?:$literal)?/s", $statement, $fragments, PREG_OFFSET_CAPTURE); + + return $fragments[1]; + } + + /** + * @param string $paramName The name of the parameter (without a colon in front) + * @param array $paramsOrTypes A hash of parameters or types + * @param bool $isParam + * @param mixed $defaultValue An optional default value. If omitted, an exception is thrown + * + * @throws SQLParserUtilsException + * @return mixed + */ + static private function extractParam($paramName, $paramsOrTypes, $isParam, $defaultValue = null) + { + if (array_key_exists($paramName, $paramsOrTypes)) { + return $paramsOrTypes[$paramName]; + } + + // Hash keys can be prefixed with a colon for compatibility + if (array_key_exists(':' . $paramName, $paramsOrTypes)) { + return $paramsOrTypes[':' . $paramName]; + } + + if (null !== $defaultValue) { + return $defaultValue; + } + + if ($isParam) { + throw SQLParserUtilsException::missingParam($paramName); + } + + throw SQLParserUtilsException::missingType($paramName); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php new file mode 100644 index 00000000000..25c7209c7be --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +class SQLParserUtilsException extends DBALException +{ + /** + * @param string $paramName + * + * @return \Doctrine\DBAL\SQLParserUtilsException + */ + public static function missingParam($paramName) + { + return new self(sprintf('Value for :%1$s not found in params array. Params array key should be "%1$s"', $paramName)); + } + + /** + * @param string $typeName + * + * @return \Doctrine\DBAL\SQLParserUtilsException + */ + public static function missingType($typeName) + { + return new self(sprintf('Value for :%1$s not found in types array. Types array key should be "%1$s"', $typeName)); + } +} 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 00000000000..051dce92c4f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php @@ -0,0 +1,227 @@ +. + */ + +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 + * + * @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|null + */ + protected $_namespace = null; + + /** + * @var boolean + */ + protected $_quoted = false; + + /** + * Sets the name of this asset. + * + * @param string $name + * + * @return void + */ + protected function _setName($name) + { + if ($this->isIdentifierQuoted($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 boolean + */ + public function isInDefaultNamespace($defaultNamespaceName) + { + return $this->_namespace == $defaultNamespaceName || $this->_namespace === null; + } + + /** + * Gets the namespace name of this asset. + * + * If NULL is returned this means the default namespace is used. + * + * @return string|null + */ + 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 $defaultNamespaceName + * + * @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. + * + * @param string $defaultNamespaceName + * + * @return string + */ + public function getFullQualifiedName($defaultNamespaceName) + { + $name = $this->getName(); + if ( ! $this->_namespace) { + $name = $defaultNamespaceName . "." . $name; + } + + return strtolower($name); + } + + /** + * Checks if this asset's name is quoted. + * + * @return boolean + */ + public function isQuoted() + { + return $this->_quoted; + } + + /** + * Checks if this identifier is quoted. + * + * @param string $identifier + * + * @return boolean + */ + protected function isIdentifierQuoted($identifier) + { + return (isset($identifier[0]) && ($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); + } + + /** + * Returns the name of this schema asset. + * + * @return string + */ + public function getName() + { + if ($this->_namespace) { + return $this->_namespace . "." . $this->_name; + } + + return $this->_name; + } + + /** + * Gets the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param \Doctrine\DBAL\Platforms\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); + } + + /** + * Generates 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 integer $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 00000000000..19dfa9369b4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -0,0 +1,1113 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +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. + * + * @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 + * @param \Doctrine\DBAL\Platforms\AbstractPlatform|null $platform + */ + public function __construct(\Doctrine\DBAL\Connection $conn, AbstractPlatform $platform = null) + { + $this->_conn = $conn; + $this->_platform = $platform ?: $this->_conn->getDatabasePlatform(); + } + + /** + * Returns the associated platform. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Tries 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; + } + } + + /** + * Lists the available databases for this connection. + * + * @return array + */ + public function listDatabases() + { + $sql = $this->_platform->getListDatabasesSQL(); + + $databases = $this->_conn->fetchAll($sql); + + return $this->_getPortableDatabasesList($databases); + } + + /** + * Returns a list of all namespaces in the current database. + * + * @return array + */ + public function listNamespaceNames() + { + $sql = $this->_platform->getListNamespacesSQL(); + + $namespaces = $this->_conn->fetchAll($sql); + + return $this->getPortableNamespacesList($namespaces); + } + + /** + * Lists the available sequences for this connection. + * + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\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)); + } + + /** + * Lists 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|null $database + * + * @return \Doctrine\DBAL\Schema\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); + } + + /** + * Lists 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 \Doctrine\DBAL\Schema\Index[] + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + $tableIndexes = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * Returns true if all the given tables exist. + * + * @param array $tableNames + * + * @return boolean + */ + public function tablesExist($tableNames) + { + $tableNames = array_map('strtolower', (array) $tableNames); + + return count($tableNames) == count(\array_intersect($tableNames, array_map('strtolower', $this->listTableNames()))); + } + + /** + * Returns 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); + } + + /** + * Filters 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); + }) + ); + } + + /** + * @return string|null + */ + protected function getFilterSchemaAssetsExpression() + { + return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression(); + } + + /** + * Lists the tables for this connection. + * + * @return \Doctrine\DBAL\Schema\Table[] + */ + public function listTables() + { + $tableNames = $this->listTableNames(); + + $tables = array(); + foreach ($tableNames as $tableName) { + $tables[] = $this->listTableDetails($tableName); + } + + return $tables; + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\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()); + } + + /** + * Lists the views this connection has. + * + * @return \Doctrine\DBAL\Schema\View[] + */ + public function listViews() + { + $database = $this->_conn->getDatabase(); + $sql = $this->_platform->getListViewsSQL($database); + $views = $this->_conn->fetchAll($sql); + + return $this->_getPortableViewsList($views); + } + + /** + * Lists the foreign keys for the given table. + * + * @param string $table The name of the table. + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\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. + * + * @return void + */ + public function dropDatabase($database) + { + $this->_execSql($this->_platform->getDropDatabaseSQL($database)); + } + + /** + * Drops the given table. + * + * @param string $tableName The name of the table to drop. + * + * @return void + */ + public function dropTable($tableName) + { + $this->_execSql($this->_platform->getDropTableSQL($tableName)); + } + + /** + * Drops the index from the given table. + * + * @param \Doctrine\DBAL\Schema\Index|string $index The name of the index. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table. + * + * @return void + */ + public function dropIndex($index, $table) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this->_platform); + } + + $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); + } + + /** + * Drops the constraint from the given table. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table. + * + * @return void + */ + public function dropConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); + } + + /** + * Drops a foreign key from a table. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey The name of the foreign key. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table with the foreign key. + * + * @return void + */ + 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. + * + * @return void + */ + public function dropSequence($name) + { + $this->_execSql($this->_platform->getDropSequenceSQL($name)); + } + + /** + * Drops a view. + * + * @param string $name The name of the view. + * + * @return void + */ + 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. + * + * @return void + */ + public function createDatabase($database) + { + $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + } + + /** + * Creates a new table. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function createTable(Table $table) + { + $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS; + $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); + } + + /** + * Creates a new sequence. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If something fails at database level. + */ + public function createSequence($sequence) + { + $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + } + + /** + * Creates a constraint on a table. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return void + */ + public function createConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); + } + + /** + * Creates a new index on a table. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return void + */ + public function createIndex(Index $index, $table) + { + $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); + } + + /** + * Creates a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The ForeignKey instance. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return void + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); + } + + /** + * Creates a new view. + * + * @param \Doctrine\DBAL\Schema\View $view + * + * @return void + */ + public function createView(View $view) + { + $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); + } + + /* dropAndCreate*() Methods */ + + /** + * Drops and creates a constraint. + * + * @see dropConstraint() + * @see createConstraint() + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return void + */ + public function dropAndCreateConstraint(Constraint $constraint, $table) + { + $this->tryMethod('dropConstraint', $constraint, $table); + $this->createConstraint($constraint, $table); + } + + /** + * Drops and creates a new index on a table. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return void + */ + public function dropAndCreateIndex(Index $index, $table) + { + $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); + $this->createIndex($index, $table); + } + + /** + * Drops and creates a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return void + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->tryMethod('dropForeignKey', $foreignKey, $table); + $this->createForeignKey($foreignKey, $table); + } + + /** + * Drops and create a new sequence. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @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); + } + + /** + * Drops and creates a new table. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function dropAndCreateTable(Table $table) + { + $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); + $this->createTable($table); + } + + /** + * Drops and creates a new database. + * + * @param string $database The name of the database to create. + * + * @return void + */ + public function dropAndCreateDatabase($database) + { + $this->tryMethod('dropDatabase', $database); + $this->createDatabase($database); + } + + /** + * Drops and creates a new view. + * + * @param \Doctrine\DBAL\Schema\View $view + * + * @return void + */ + public function dropAndCreateView(View $view) + { + $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); + $this->createView($view); + } + + /* alterTable() Methods */ + + /** + * Alters an existing tables schema. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * + * @return void + */ + public function alterTable(TableDiff $tableDiff) + { + $queries = $this->_platform->getAlterTableSQL($tableDiff); + if (is_array($queries) && count($queries)) { + foreach ($queries as $ddlQuery) { + $this->_execSql($ddlQuery); + } + } + } + + /** + * Renames a given table to another name. + * + * @param string $name The current name of the table. + * @param string $newName The new name of the table. + * + * @return void + */ + 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 + */ + + /** + * @param array $databases + * + * @return array + */ + protected function _getPortableDatabasesList($databases) + { + $list = array(); + foreach ($databases as $value) { + if ($value = $this->_getPortableDatabaseDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. + * + * @param array $namespaces The list of namespace names in the native DBMS data definition. + * + * @return array + */ + protected function getPortableNamespacesList(array $namespaces) + { + $namespacesList = array(); + + foreach ($namespaces as $namespace) { + $namespacesList[] = $this->getPortableNamespaceDefinition($namespace); + } + + return $namespacesList; + } + + /** + * @param array $database + * + * @return mixed + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database; + } + + /** + * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. + * + * @param array $namespace The native DBMS namespace definition. + * + * @return mixed + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace; + } + + /** + * @param array $functions + * + * @return array + */ + protected function _getPortableFunctionsList($functions) + { + $list = array(); + foreach ($functions as $value) { + if ($value = $this->_getPortableFunctionDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $function + * + * @return mixed + */ + protected function _getPortableFunctionDefinition($function) + { + return $function; + } + + /** + * @param array $triggers + * + * @return array + */ + protected function _getPortableTriggersList($triggers) + { + $list = array(); + foreach ($triggers as $value) { + if ($value = $this->_getPortableTriggerDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $trigger + * + * @return mixed + */ + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger; + } + + /** + * @param array $sequences + * + * @return array + */ + protected function _getPortableSequencesList($sequences) + { + $list = array(); + foreach ($sequences as $value) { + if ($value = $this->_getPortableSequenceDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $sequence + * + * @return \Doctrine\DBAL\Schema\Sequence + * + * @throws \Doctrine\DBAL\DBALException + */ + 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 $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; + } + + /** + * Gets Table Column Definition. + * + * @param array $tableColumn + * + * @return \Doctrine\DBAL\Schema\Column + */ + abstract protected function _getPortableTableColumnDefinition($tableColumn); + + /** + * Aggregates and groups the index results according to the required data result. + * + * @param array $tableIndexRows + * @param string|null $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'], + 'flags' => isset($tableIndex['flags']) ? $tableIndex['flags'] : array(), + 'options' => isset($tableIndex['where']) ? array('where' => $tableIndex['where']) : array(), + ); + } 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'], $data['flags'], $data['options']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + /** + * @param array $tables + * + * @return array + */ + protected function _getPortableTablesList($tables) + { + $list = array(); + foreach ($tables as $value) { + if ($value = $this->_getPortableTableDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $table + * + * @return array + */ + protected function _getPortableTableDefinition($table) + { + return $table; + } + + /** + * @param array $users + * + * @return array + */ + protected function _getPortableUsersList($users) + { + $list = array(); + foreach ($users as $value) { + if ($value = $this->_getPortableUserDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $user + * + * @return mixed + */ + protected function _getPortableUserDefinition($user) + { + return $user; + } + + /** + * @param array $views + * + * @return array + */ + protected function _getPortableViewsList($views) + { + $list = array(); + foreach ($views as $value) { + if ($view = $this->_getPortableViewDefinition($value)) { + $viewName = strtolower($view->getQuotedName($this->_platform)); + $list[$viewName] = $view; + } + } + + return $list; + } + + /** + * @param array $view + * + * @return mixed + */ + protected function _getPortableViewDefinition($view) + { + return false; + } + + /** + * @param array $tableForeignKeys + * + * @return array + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + if ($value = $this->_getPortableTableForeignKeyDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $tableForeignKey + * + * @return mixed + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return $tableForeignKey; + } + + /** + * @param array|string $sql + * + * @return void + */ + protected function _execSql($sql) + { + foreach ((array) $sql as $query) { + $this->_conn->executeUpdate($query); + } + } + + /** + * Creates a schema instance for the current database. + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function createSchema() + { + $namespaces = array(); + + if ($this->_platform->supportsSchemas()) { + $namespaces = $this->listNamespaceNames(); + } + + $sequences = array(); + + if ($this->_platform->supportsSequences()) { + $sequences = $this->listSequences(); + } + + $tables = $this->listTables(); + + return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces); + } + + /** + * Creates the configuration for this schema. + * + * @return \Doctrine\DBAL\Schema\SchemaConfig + */ + public function createSchemaConfig() + { + $schemaConfig = new SchemaConfig(); + $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); + + $searchPaths = $this->getSchemaSearchPaths(); + if (isset($searchPaths[0])) { + $schemaConfig->setName($searchPaths[0]); + } + + $params = $this->_conn->getParams(); + if (isset($params['defaultTableOptions'])) { + $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']); + } + + 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; + } + + /** + * @param string $comment + * @param string $type + * + * @return string + */ + 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 00000000000..192ce1d2eef --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php @@ -0,0 +1,487 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Object representation of a database column. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Column extends AbstractAsset +{ + /** + * @var Type + */ + protected $_type; + + /** + * @var integer|null + */ + protected $_length = null; + + /** + * @var integer + */ + protected $_precision = 10; + + /** + * @var integer + */ + protected $_scale = 0; + + /** + * @var boolean + */ + protected $_unsigned = false; + + /** + * @var boolean + */ + protected $_fixed = false; + + /** + * @var boolean + */ + protected $_notnull = true; + + /** + * @var string|null + */ + protected $_default = null; + + /** + * @var boolean + */ + protected $_autoincrement = false; + + /** + * @var array + */ + protected $_platformOptions = array(); + + /** + * @var string|null + */ + protected $_columnDefinition = null; + + /** + * @var string|null + */ + protected $_comment = null; + + /** + * @var array + */ + protected $_customSchemaOptions = array(); + + /** + * Creates a new Column. + * + * @param string $columnName + * @param Type $type + * @param array $options + */ + 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 integer|null $length + * + * @return Column + */ + public function setLength($length) + { + if ($length !== null) { + $this->_length = (int) $length; + } else { + $this->_length = null; + } + + return $this; + } + + /** + * @param integer $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 integer $scale + * + * @return Column + */ + public function setScale($scale) + { + if (!is_numeric($scale)) { + $scale = 0; + } + + $this->_scale = (int) $scale; + + return $this; + } + + /** + * @param boolean $unsigned + * + * @return Column + */ + public function setUnsigned($unsigned) + { + $this->_unsigned = (bool) $unsigned; + + return $this; + } + + /** + * @param boolean $fixed + * + * @return Column + */ + public function setFixed($fixed) + { + $this->_fixed = (bool) $fixed; + + return $this; + } + + /** + * @param boolean $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 $value + * + * @return Column + */ + public function setColumnDefinition($value) + { + $this->_columnDefinition = $value; + + return $this; + } + + /** + * @return Type + */ + public function getType() + { + return $this->_type; + } + + /** + * @return integer|null + */ + public function getLength() + { + return $this->_length; + } + + /** + * @return integer + */ + public function getPrecision() + { + return $this->_precision; + } + + /** + * @return integer + */ + public function getScale() + { + return $this->_scale; + } + + /** + * @return boolean + */ + public function getUnsigned() + { + return $this->_unsigned; + } + + /** + * @return boolean + */ + public function getFixed() + { + return $this->_fixed; + } + + /** + * @return boolean + */ + public function getNotnull() + { + return $this->_notnull; + } + + /** + * @return string|null + */ + public function getDefault() + { + return $this->_default; + } + + /** + * @return array + */ + public function getPlatformOptions() + { + return $this->_platformOptions; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasPlatformOption($name) + { + return isset($this->_platformOptions[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getPlatformOption($name) + { + return $this->_platformOptions[$name]; + } + + /** + * @return string|null + */ + public function getColumnDefinition() + { + return $this->_columnDefinition; + } + + /** + * @return boolean + */ + public function getAutoincrement() + { + return $this->_autoincrement; + } + + /** + * @param boolean $flag + * + * @return Column + */ + public function setAutoincrement($flag) + { + $this->_autoincrement = $flag; + + return $this; + } + + /** + * @param string $comment + * + * @return Column + */ + public function setComment($comment) + { + $this->_comment = $comment; + + return $this; + } + + /** + * @return string|null + */ + 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; + } + + /** + * @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); + } +} 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 00000000000..c993c9b1189 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Represents the change of a column. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class ColumnDiff +{ + /** + * @var string + */ + public $oldColumnName; + + /** + * @var Column + */ + public $column; + + /** + * @var array + */ + public $changedProperties = array(); + + /** + * @var Column + */ + public $fromColumn; + + /** + * @param string $oldColumnName + * @param Column $column + * @param string[] $changedProperties + * @param Column $fromColumn + */ + public function __construct($oldColumnName, Column $column, array $changedProperties = array(), Column $fromColumn = null) + { + $this->oldColumnName = $oldColumnName; + $this->column = $column; + $this->changedProperties = $changedProperties; + $this->fromColumn = $fromColumn; + } + + /** + * @param string $propertyName + * + * @return boolean + */ + public function hasChanged($propertyName) + { + return in_array($propertyName, $this->changedProperties); + } + + /** + * @return Identifier + */ + public function getOldColumnName() + { + return new Identifier($this->oldColumnName); + } +} 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 00000000000..4f11e6c8dfe --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php @@ -0,0 +1,515 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types; + +/** + * Compares two Schemas and return an instance of SchemaDiff. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Comparator +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * + * @return \Doctrine\DBAL\Schema\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 differences 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 \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + public function compare(Schema $fromSchema, Schema $toSchema) + { + $diff = new SchemaDiff(); + $diff->fromSchema = $fromSchema; + + $foreignKeysToTable = array(); + + foreach ($toSchema->getNamespaces() as $namespace) { + if ( ! $fromSchema->hasNamespace($namespace)) { + $diff->newNamespaces[$namespace] = $namespace; + } + } + + foreach ($fromSchema->getNamespaces() as $namespace) { + if ( ! $toSchema->hasNamespace($namespace)) { + $diff->removedNamespaces[$namespace] = $namespace; + } + } + + 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]); + + // deleting duplicated foreign keys present on both on the orphanedForeignKey + // and the removedForeignKeys from changedTables + foreach ($foreignKeysToTable[$tableName] as $foreignKey) { + // strtolower the table name to make if compatible with getShortestName + $localTableName = strtolower($foreignKey->getLocalTableName()); + if (isset($diff->changedTables[$localTableName])) { + foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) { + unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]); + } + } + } + } + } + + foreach ($toSchema->getSequences() as $sequence) { + $sequenceName = $sequence->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasSequence($sequenceName)) { + if ( ! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) { + $diff->newSequences[] = $sequence; + } + } else { + if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { + $diff->changedSequences[] = $toSchema->getSequence($sequenceName); + } + } + } + + foreach ($fromSchema->getSequences() as $sequence) { + if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) { + continue; + } + + $sequenceName = $sequence->getShortestName($fromSchema->getName()); + + if ( ! $toSchema->hasSequence($sequenceName)) { + $diff->removedSequences[] = $sequence; + } + } + + return $diff; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return boolean + */ + private function isAutoIncrementSequenceInSchema($schema, $sequence) + { + foreach ($schema->getTables() as $table) { + if ($sequence->isAutoIncrementsFor($table)) { + return true; + } + } + + return false; + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence1 + * @param \Doctrine\DBAL\Schema\Sequence $sequence2 + * + * @return boolean + */ + 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 \Doctrine\DBAL\Schema\Table $table1 + * @param \Doctrine\DBAL\Schema\Table $table2 + * + * @return boolean|\Doctrine\DBAL\Schema\TableDiff + */ + public function diffTable(Table $table1, Table $table2) + { + $changes = 0; + $tableDifferences = new TableDiff($table1->getName()); + $tableDifferences->fromTable = $table1; + + $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) { + // See if column is removed in table 2. + if ( ! $table2->hasColumn($columnName)) { + $tableDifferences->removedColumns[$columnName] = $column; + $changes++; + continue; + } + + // See if column has changed properties in table 2. + $changedProperties = $this->diffColumn($column, $table2->getColumn($columnName)); + + if ( ! empty($changedProperties)) { + $columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties); + $columnDiff->fromColumn = $column; + $tableDifferences->changedColumns[$column->getName()] = $columnDiff; + $changes++; + } + } + + $this->detectColumnRenamings($tableDifferences); + + $table1Indexes = $table1->getIndexes(); + $table2Indexes = $table2->getIndexes(); + + /* See if all the indexes in table 1 exist in table 2 */ + foreach ($table2Indexes as $indexName => $index) { + if (($index->isPrimary() && $table1->hasPrimaryKey()) || $table1->hasIndex($indexName)) { + continue; + } + + $tableDifferences->addedIndexes[$indexName] = $index; + $changes++; + } + /* See if there are any removed indexes in table 2 */ + foreach ($table1Indexes as $indexName => $index) { + // See if index is removed in table 2. + if (($index->isPrimary() && ! $table2->hasPrimaryKey()) || + ! $index->isPrimary() && ! $table2->hasIndex($indexName) + ) { + $tableDifferences->removedIndexes[$indexName] = $index; + $changes++; + continue; + } + + // See if index has changed in table 2. + $table2Index = $index->isPrimary() ? $table2->getPrimaryKey() : $table2->getIndex($indexName); + + if ($this->diffIndex($index, $table2Index)) { + $tableDifferences->changedIndexes[$indexName] = $table2Index; + $changes++; + } + } + + $this->detectIndexRenamings($tableDifferences); + + $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 $constraint1) { + $tableDifferences->removedForeignKeys[] = $constraint1; + $changes++; + } + + foreach ($toFkeys as $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 ambiguities between different possibilities should not lead to renaming at all. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences + * + * @return void + */ + private function detectColumnRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { + foreach ($tableDifferences->removedColumns as $removedColumn) { + if (count($this->diffColumn($addedColumn, $removedColumn)) == 0) { + $renameCandidates[$addedColumn->getName()][] = array($removedColumn, $addedColumn, $addedColumnName); + } + } + } + + foreach ($renameCandidates as $candidateColumns) { + if (count($candidateColumns) == 1) { + list($removedColumn, $addedColumn) = $candidateColumns[0]; + $removedColumnName = strtolower($removedColumn->getName()); + $addedColumnName = strtolower($addedColumn->getName()); + + if ( ! isset($tableDifferences->renamedColumns[$removedColumnName])) { + $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; + unset($tableDifferences->addedColumns[$addedColumnName]); + unset($tableDifferences->removedColumns[$removedColumnName]); + } + } + } + } + + /** + * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences + * + * @return void + */ + private function detectIndexRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + + // Gather possible rename candidates by comparing each added and removed index based on semantics. + foreach ($tableDifferences->addedIndexes as $addedIndexName => $addedIndex) { + foreach ($tableDifferences->removedIndexes as $removedIndex) { + if (! $this->diffIndex($addedIndex, $removedIndex)) { + $renameCandidates[$addedIndex->getName()][] = array($removedIndex, $addedIndex, $addedIndexName); + } + } + } + + foreach ($renameCandidates as $candidateIndexes) { + // If the current rename candidate contains exactly one semantically equal index, + // we can safely rename it. + // Otherwise it is unclear if a rename action is really intended, + // therefore we let those ambiguous indexes be added/dropped. + if (count($candidateIndexes) === 1) { + list($removedIndex, $addedIndex) = $candidateIndexes[0]; + + $removedIndexName = strtolower($removedIndex->getName()); + $addedIndexName = strtolower($addedIndex->getName()); + + if (! isset($tableDifferences->renamedIndexes[$removedIndexName])) { + $tableDifferences->renamedIndexes[$removedIndexName] = $addedIndex; + unset($tableDifferences->addedIndexes[$addedIndexName]); + unset($tableDifferences->removedIndexes[$removedIndexName]); + } + } + } + } + + /** + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key1 + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key2 + * + * @return boolean + */ + public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) + { + if (array_map('strtolower', $key1->getUnquotedLocalColumns()) != array_map('strtolower', $key2->getUnquotedLocalColumns())) { + return true; + } + + if (array_map('strtolower', $key1->getUnquotedForeignColumns()) != array_map('strtolower', $key2->getUnquotedForeignColumns())) { + return true; + } + + if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) { + 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 \Doctrine\DBAL\Schema\Column $column1 + * @param \Doctrine\DBAL\Schema\Column $column2 + * + * @return array + */ + public function diffColumn(Column $column1, Column $column2) + { + $properties1 = $column1->toArray(); + $properties2 = $column2->toArray(); + + $changedProperties = array(); + + foreach (array('type', 'notnull', 'unsigned', 'autoincrement') as $property) { + if ($properties1[$property] != $properties2[$property]) { + $changedProperties[] = $property; + } + } + + if ($properties1['default'] != $properties2['default'] || + // Null values need to be checked additionally as they tell whether to create or drop a default value. + // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. + (null === $properties1['default'] && null !== $properties2['default']) || + (null === $properties2['default'] && null !== $properties1['default']) + ) { + $changedProperties[] = 'default'; + } + + if (($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) || + $properties1['type'] instanceof Types\BinaryType + ) { + // check if value of length is set at all, default value assumed otherwise. + $length1 = $properties1['length'] ?: 255; + $length2 = $properties2['length'] ?: 255; + if ($length1 != $length2) { + $changedProperties[] = 'length'; + } + + if ($properties1['fixed'] != $properties2['fixed']) { + $changedProperties[] = 'fixed'; + } + } elseif ($properties1['type'] instanceof Types\DecimalType) { + if (($properties1['precision'] ?: 10) != ($properties2['precision'] ?: 10)) { + $changedProperties[] = 'precision'; + } + if ($properties1['scale'] != $properties2['scale']) { + $changedProperties[] = 'scale'; + } + } + + // A null value and an empty string are actually equal for a comment so they should not trigger a change. + if ($properties1['comment'] !== $properties2['comment'] && + ! (null === $properties1['comment'] && '' === $properties2['comment']) && + ! (null === $properties2['comment'] && '' === $properties1['comment']) + ) { + $changedProperties[] = 'comment'; + } + + $customOptions1 = $column1->getCustomSchemaOptions(); + $customOptions2 = $column2->getCustomSchemaOptions(); + + foreach (array_merge(array_keys($customOptions1), array_keys($customOptions2)) as $key) { + if ( ! array_key_exists($key, $properties1) || ! array_key_exists($key, $properties2)) { + $changedProperties[] = $key; + } elseif ($properties1[$key] !== $properties2[$key]) { + $changedProperties[] = $key; + } + } + + $platformOptions1 = $column1->getPlatformOptions(); + $platformOptions2 = $column2->getPlatformOptions(); + + foreach (array_keys(array_intersect_key($platformOptions1, $platformOptions2)) as $key) { + if ($properties1[$key] !== $properties2[$key]) { + $changedProperties[] = $key; + } + } + + return array_unique($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 \Doctrine\DBAL\Schema\Index $index1 + * @param \Doctrine\DBAL\Schema\Index $index2 + * + * @return boolean + */ + 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 00000000000..c373472c465 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Marker interface for contraints. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +interface Constraint +{ + /** + * @return string + */ + public function getName(); + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedName(AbstractPlatform $platform); + + /** + * Returns the names of the referencing table columns + * the constraint is associated with. + * + * @return array + */ + public function getColumns(); + + /** + * Returns the quoted representation of the column names + * the constraint is associated with. + * + * But only if they were defined with one or a column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedColumns(AbstractPlatform $platform); +} 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 00000000000..ff914c60f9c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -0,0 +1,210 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * IBM Db2 Schema Manager. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class DB2SchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * 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} + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')"; + + $tables = $this->_conn->fetchAll($sql); + + return $this->_getPortableTablesList($tables); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, \CASE_LOWER); + + $length = null; + $fixed = null; + $unsigned = false; + $scale = false; + $precision = false; + + $default = null; + + if (null !== $tableColumn['default'] && 'NULL' != $tableColumn['default']) { + $default = trim($tableColumn['default'], "'"); + } + + $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' => $default, + 'autoincrement' => (boolean) $tableColumn['autoincrement'], + '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); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTablesList($tables) + { + $tableNames = array(); + foreach ($tables as $tableRow) { + $tableRow = array_change_key_case($tableRow, \CASE_LOWER); + $tableNames[] = $tableRow['name']; + } + + return $tableNames; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null) + { + foreach ($tableIndexRows as &$tableIndexRow) { + $tableIndexRow = array_change_key_case($tableIndexRow, \CASE_LOWER); + $tableIndexRow['primary'] = (boolean) $tableIndexRow['primary']; + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + $tableForeignKey = array_change_key_case($tableForeignKey, \CASE_LOWER); + + if (!isset($foreignKeys[$tableForeignKey['index_name']])) { + $foreignKeys[$tableForeignKey['index_name']] = array( + 'local_columns' => array($tableForeignKey['local_column']), + 'foreign_table' => $tableForeignKey['foreign_table'], + 'foreign_columns' => array($tableForeignKey['foreign_column']), + 'name' => $tableForeignKey['index_name'], + 'options' => array( + 'onUpdate' => $tableForeignKey['on_update'], + 'onDelete' => $tableForeignKey['on_delete'], + ) + ); + } else { + $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; + $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } elseif ($def == "N") { + return "SET NULL"; + } + + return null; + } + + /** + * {@inheritdoc} + */ + 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); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php new file mode 100644 index 00000000000..e9503c359a9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Schema manager for the Drizzle RDBMS. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtolower($tableColumn['DATA_TYPE']); + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + $tableColumn['COLUMN_COMMENT'] = $this->removeDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + + $options = array( + 'notnull' => !(bool) $tableColumn['IS_NULLABLE'], + 'length' => (int) $tableColumn['CHARACTER_MAXIMUM_LENGTH'], + 'default' => isset($tableColumn['COLUMN_DEFAULT']) ? $tableColumn['COLUMN_DEFAULT'] : null, + 'autoincrement' => (bool) $tableColumn['IS_AUTO_INCREMENT'], + 'scale' => (int) $tableColumn['NUMERIC_SCALE'], + 'precision' => (int) $tableColumn['NUMERIC_PRECISION'], + 'comment' => isset($tableColumn['COLUMN_COMMENT']) && '' !== $tableColumn['COLUMN_COMMENT'] + ? $tableColumn['COLUMN_COMMENT'] + : null, + ); + + $column = new Column($tableColumn['COLUMN_NAME'], Type::getType($type), $options); + + if ( ! empty($tableColumn['COLLATION_NAME'])) { + $column->setPlatformOption('collation', $tableColumn['COLLATION_NAME']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['SCHEMA_NAME']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['TABLE_NAME']; + } + + /** + * {@inheritdoc} + */ + public function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $columns = array(); + foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) { + $columns[] = trim($value, ' `'); + } + + $refColumns = array(); + foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) { + $refColumns[] = trim($value, ' `'); + } + + return new ForeignKeyConstraint( + $columns, + $tableForeignKey['REFERENCED_TABLE_NAME'], + $refColumns, + $tableForeignKey['CONSTRAINT_NAME'], + array( + 'onUpdate' => $tableForeignKey['UPDATE_RULE'], + 'onDelete' => $tableForeignKey['DELETE_RULE'], + ) + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + { + $indexes = array(); + foreach ($tableIndexes as $k) { + $k['primary'] = (boolean) $k['primary']; + $indexes[] = $k; + } + + return parent::_getPortableTableIndexesList($indexes, $tableName); + } +} 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 00000000000..6070eb2e5ba --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -0,0 +1,389 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * An abstraction class for a foreign key constraint. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.0 + */ +class ForeignKeyConstraint extends AbstractAsset implements Constraint +{ + /** + * Instance of the referencing table the foreign key constraint is associated with. + * + * @var \Doctrine\DBAL\Schema\Table + */ + protected $_localTable; + + /** + * Asset identifier instances of the referencing table column names the foreign key constraint is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_localColumnNames; + + /** + * Table or asset identifier instance of the referenced table name the foreign key constraint is associated with. + * + * @var Table|Identifier + */ + protected $_foreignTableName; + + /** + * Asset identifier instances of the referenced table column names the foreign key constraint is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_foreignColumnNames; + + /** + * @var array Options associated with the foreign key constraint. + */ + protected $_options; + + /** + * Initializes the foreign key constraint. + * + * @param array $localColumnNames Names of the referencing table columns. + * @param Table|string $foreignTableName Referenced table. + * @param array $foreignColumnNames Names of the referenced table columns. + * @param string|null $name Name of the foreign key constraint. + * @param array $options Options associated with the foreign key constraint. + */ + public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name = null, array $options = array()) + { + $this->_setName($name); + $identifierConstructorCallback = function ($column) { + return new Identifier($column); + }; + $this->_localColumnNames = $localColumnNames + ? array_combine($localColumnNames, array_map($identifierConstructorCallback, $localColumnNames)) + : array(); + + if ($foreignTableName instanceof Table) { + $this->_foreignTableName = $foreignTableName; + } else { + $this->_foreignTableName = new Identifier($foreignTableName); + } + + $this->_foreignColumnNames = $foreignColumnNames + ? array_combine($foreignColumnNames, array_map($identifierConstructorCallback, $foreignColumnNames)) + : array(); + $this->_options = $options; + } + + /** + * Returns the name of the referencing table + * the foreign key constraint is associated with. + * + * @return string + */ + public function getLocalTableName() + { + return $this->_localTable->getName(); + } + + /** + * Sets the Table instance of the referencing table + * the foreign key constraint is associated with. + * + * @param \Doctrine\DBAL\Schema\Table $table Instance of the referencing table. + * + * @return void + */ + public function setLocalTable(Table $table) + { + $this->_localTable = $table; + } + + /** + * @return Table + */ + public function getLocalTable() + { + return $this->_localTable; + } + + /** + * Returns the names of the referencing table columns + * the foreign key constraint is associated with. + * + * @return array + */ + public function getLocalColumns() + { + return array_keys($this->_localColumnNames); + } + + /** + * Returns the quoted representation of the referencing table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referencing table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedLocalColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_localColumnNames as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * Returns unquoted representation of local table column names for comparison with other FK + * + * @return array + */ + public function getUnquotedLocalColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getLocalColumns()); + } + + /** + * Returns unquoted representation of foreign table column names for comparison with other FK + * + * @return array + */ + public function getUnquotedForeignColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getForeignColumns()); + } + + /** + * {@inheritdoc} + * + * @see getLocalColumns + */ + public function getColumns() + { + return $this->getLocalColumns(); + } + + /** + * Returns the quoted representation of the referencing table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referencing table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @see getQuotedLocalColumns + * + * @return array + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + return $this->getQuotedLocalColumns($platform); + } + + /** + * Returns the name of the referenced table + * the foreign key constraint is associated with. + * + * @return string + */ + public function getForeignTableName() + { + return $this->_foreignTableName->getName(); + } + + /** + * Returns the non-schema qualified foreign table name. + * + * @return string + */ + public function getUnqualifiedForeignTableName() + { + $parts = explode(".", $this->_foreignTableName->getName()); + + return strtolower(end($parts)); + } + + /** + * Returns the quoted representation of the referenced table name + * the foreign key constraint is associated with. + * + * But only if it was defined with one or the referenced table name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return string + */ + public function getQuotedForeignTableName(AbstractPlatform $platform) + { + return $this->_foreignTableName->getQuotedName($platform); + } + + /** + * Returns the names of the referenced table columns + * the foreign key constraint is associated with. + * + * @return array + */ + public function getForeignColumns() + { + return array_keys($this->_foreignColumnNames); + } + + /** + * Returns the quoted representation of the referenced table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referenced table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedForeignColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_foreignColumnNames as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * Returns whether or not a given option + * is associated with the foreign key constraint. + * + * @param string $name Name of the option to check. + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + /** + * Returns an option associated with the foreign key constraint. + * + * @param string $name Name of the option the foreign key constraint is associated with. + * + * @return mixed + */ + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * Returns the options associated with the foreign key constraint. + * + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * Returns the referential action for UPDATE operations + * on the referenced table the foreign key constraint is associated with. + * + * @return string|null + */ + public function onUpdate() + { + return $this->onEvent('onUpdate'); + } + + /** + * Returns the referential action for DELETE operations + * on the referenced table the foreign key constraint is associated with. + * + * @return string|null + */ + public function onDelete() + { + return $this->onEvent('onDelete'); + } + + /** + * Returns the referential action for a given database operation + * on the referenced table the foreign key constraint is associated with. + * + * @param string $event Name of the database operation/event to return the referential action for. + * + * @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; + } + + /** + * Checks whether this foreign key constraint intersects the given index columns. + * + * Returns `true` if at least one of this foreign key's local columns + * matches one of the given index's columns, `false` otherwise. + * + * @param Index $index The index to be checked against. + * + * @return boolean + */ + public function intersectsIndexColumns(Index $index) + { + foreach ($index->getColumns() as $indexColumn) { + foreach ($this->_localColumnNames as $localColumn) { + if (strtolower($indexColumn) === strtolower($localColumn->getName())) { + return true; + } + } + } + + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php new file mode 100644 index 00000000000..1e48614660e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * An abstraction class for an asset identifier. + * + * Wraps identifier names like column names in indexes / foreign keys + * in an abstract class for proper quotation capabilities. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.4 + */ +class Identifier extends AbstractAsset +{ + /** + * Constructor. + * + * @param string $identifier Identifier name to wrap. + */ + public function __construct($identifier) + { + $this->_setName($identifier); + } +} 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 00000000000..bc5cd93dc6f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php @@ -0,0 +1,358 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class Index extends AbstractAsset implements Constraint +{ + /** + * Asset identifier instances of the column names the index is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_columns = array(); + + /** + * @var boolean + */ + protected $_isUnique = false; + + /** + * @var boolean + */ + protected $_isPrimary = false; + + /** + * Platform specific flags for indexes. + * array($flagName => true) + * + * @var array + */ + protected $_flags = array(); + + /** + * Platform specific options + * + * @todo $_flags should eventually be refactored into options + * + * @var array + */ + private $options = array(); + + /** + * @param string $indexName + * @param string[] $columns + * @param boolean $isUnique + * @param boolean $isPrimary + * @param string[] $flags + * @param array $options + */ + public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = array(), array $options = array()) + { + $isUnique = $isUnique || $isPrimary; + + $this->_setName($indexName); + $this->_isUnique = $isUnique; + $this->_isPrimary = $isPrimary; + $this->options = $options; + + foreach ($columns as $column) { + $this->_addColumn($column); + } + foreach ($flags as $flag) { + $this->addFlag($flag); + } + } + + /** + * @param string $column + * + * @return void + * + * @throws \InvalidArgumentException + */ + protected function _addColumn($column) + { + if (is_string($column)) { + $this->_columns[$column] = new Identifier($column); + } else { + throw new \InvalidArgumentException("Expecting a string as Index Column"); + } + } + + /** + * {@inheritdoc} + */ + public function getColumns() + { + return array_keys($this->_columns); + } + + /** + * {@inheritdoc} + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_columns as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * @return string[] + */ + public function getUnquotedColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getColumns()); + } + + /** + * Is the index neither unique nor primary key? + * + * @return boolean + */ + public function isSimpleIndex() + { + return !$this->_isPrimary && !$this->_isUnique; + } + + /** + * @return boolean + */ + public function isUnique() + { + return $this->_isUnique; + } + + /** + * @return boolean + */ + public function isPrimary() + { + return $this->_isPrimary; + } + + /** + * @param string $columnName + * @param integer $pos + * + * @return boolean + */ + public function hasColumnAtPosition($columnName, $pos = 0) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); + + return array_search($columnName, $indexColumns) === $pos; + } + + /** + * Checks if this index exactly spans the given column names in the correct order. + * + * @param array $columnNames + * + * @return boolean + */ + public function spansColumns(array $columnNames) + { + $columns = $this->getColumns(); + $numberOfColumns = count($columns); + $sameColumns = true; + + for ($i = 0; $i < $numberOfColumns; $i++) { + if ( ! isset($columnNames[$i]) || $this->trimQuotes(strtolower($columns[$i])) !== $this->trimQuotes(strtolower($columnNames[$i]))) { + $sameColumns = false; + } + } + + return $sameColumns; + } + + /** + * Checks if the other index already fulfills all the indexing and constraint needs of the current one. + * + * @param Index $other + * + * @return boolean + */ + 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->samePartialIndex($other)) { + return false; + } + + 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 fulfill the requirements of just an + // index that has no constraints. + return true; + } + + if ($other->isPrimary() != $this->isPrimary()) { + return false; + } + + if ($other->isUnique() != $this->isUnique()) { + return false; + } + + return true; + } + + return false; + } + + /** + * Detects if the other index is a non-unique, non primary index that can be overwritten by this one. + * + * @param Index $other + * + * @return boolean + */ + public function overrules(Index $other) + { + if ($other->isPrimary()) { + return false; + } elseif ($this->isSimpleIndex() && $other->isUnique()) { + return false; + } + + if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other)) { + return true; + } + + return false; + } + + /** + * Returns platform specific flags for indexes. + * + * @return string[] + */ + public function getFlags() + { + return array_keys($this->_flags); + } + + /** + * Adds Flag for an index that translates to platform specific handling. + * + * @example $index->addFlag('CLUSTERED') + * + * @param string $flag + * + * @return Index + */ + public function addFlag($flag) + { + $this->_flags[strtolower($flag)] = true; + + return $this; + } + + /** + * Does this index have a specific flag? + * + * @param string $flag + * + * @return boolean + */ + public function hasFlag($flag) + { + return isset($this->_flags[strtolower($flag)]); + } + + /** + * Removes a flag. + * + * @param string $flag + * + * @return void + */ + public function removeFlag($flag) + { + unset($this->_flags[strtolower($flag)]); + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->options[strtolower($name)]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->options[strtolower($name)]; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Return whether the two indexes have the same partial index + * @param \Doctrine\DBAL\Schema\Index $other + * + * @return boolean + */ + private function samePartialIndex(Index $other) + { + if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') == $other->getOption('where')) { + return true; + } + + if ( ! $this->hasOption('where') && ! $other->hasOption('where')) { + return true; + } + + return false; + } + +} 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 00000000000..0b36762b6ce --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -0,0 +1,252 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Types\Type; + +/** + * Schema manager for the MySql RDBMS. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class MySqlSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['User'], + 'password' => $user['Password'], + ); + } + + /** + * {@inheritdoc} + */ + 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; + } + if (strpos($v['index_type'], 'FULLTEXT') !== false) { + $v['flags'] = array('FULLTEXT'); + } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { + $v['flags'] = array('SPATIAL'); + } + $tableIndexes[$k] = $v; + } + + return parent::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return end($sequence); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['Database']; + } + + /** + * {@inheritdoc} + */ + 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']; + } else { + $length = strtok('(), '); + } + + $fixed = null; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $scale = null; + $precision = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + + // In cases where not connected to a database DESCRIBE $table does not return 'Comment' + if (isset($tableColumn['comment'])) { + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + } + + switch ($dbType) { + case 'char': + case 'binary': + $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 'tinytext': + $length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT; + break; + case 'text': + $length = MySqlPlatform::LENGTH_LIMIT_TEXT; + break; + case 'mediumtext': + $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT; + break; + case 'tinyblob': + $length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB; + break; + case 'blob': + $length = MySqlPlatform::LENGTH_LIMIT_BLOB; + break; + case 'mediumblob': + $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB; + break; + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + case 'year': + $length = null; + break; + } + + $length = ((int) $length == 0) ? null : (int) $length; + + $options = array( + 'length' => $length, + 'unsigned' => (bool) (strpos($tableColumn['type'], 'unsigned') !== false), + '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'] !== '' + ? $tableColumn['comment'] + : null, + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $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 00000000000..a02933d241d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -0,0 +1,357 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Oracle Schema Manager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class OracleSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + $view = \array_change_key_case($view, CASE_LOWER); + + return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + $user = \array_change_key_case($user, CASE_LOWER); + + return array( + 'user' => $user['username'], + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + $table = \array_change_key_case($table, CASE_LOWER); + + return $this->getQuotedIdentifierName($table['table_name']); + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + 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'] = $this->getQuotedIdentifierName($tableIndex['column_name']); + $indexBuffer[] = $buffer; + } + + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + /** + * {@inheritdoc} + */ + 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"; + } + } + + $unsigned = $fixed = null; + + if ( ! isset($tableColumn['column_name'])) { + $tableColumn['column_name'] = ''; + } + + // Default values returned from database sometimes have trailing spaces. + $tableColumn['data_default'] = trim($tableColumn['data_default']); + + if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') { + $tableColumn['data_default'] = null; + } + + if (null !== $tableColumn['data_default']) { + // Default values returned from database are enclosed in single quotes. + $tableColumn['data_default'] = trim($tableColumn['data_default'], "'"); + } + + $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': + case 'binary_float': + case 'binary_double': + $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'] + ? $tableColumn['comments'] + : null, + 'platformDetails' => array(), + ); + + return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $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' => $this->getQuotedIdentifierName($value['constraint_name']), + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['references_table'], + 'onDelete' => $value['delete_rule'], + ); + } + + $localColumn = $this->getQuotedIdentifierName($value['local_column']); + $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']); + + $list[$value['constraint_name']]['local'][$value['position']] = $localColumn; + $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; + } + + $result = array(); + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $this->getQuotedIdentifierName($constraint['foreignTable']), + array_values($constraint['foreign']), $this->getQuotedIdentifierName($constraint['name']), + array('onDelete' => $constraint['onDelete']) + ); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + $sequence = \array_change_key_case($sequence, CASE_LOWER); + + return new Sequence( + $this->getQuotedIdentifierName($sequence['sequence_name']), + $sequence['increment_by'], + $sequence['min_value'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableFunctionDefinition($function) + { + $function = \array_change_key_case($function, CASE_LOWER); + + return $function['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + $database = \array_change_key_case($database, CASE_LOWER); + + return $database['username']; + } + + /** + * {@inheritdoc} + */ + 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; + $this->_conn->executeUpdate($query); + + $query = 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' . $username; + $this->_conn->executeUpdate($query); + + return true; + } + + /** + * @param string $table + * + * @return boolean + */ + public function dropAutoincrement($table) + { + $sql = $this->_platform->getDropAutoincrementSql($table); + foreach ($sql as $query) { + $this->_conn->executeUpdate($query); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function dropTable($name) + { + $this->tryMethod('dropAutoincrement', $name); + + parent::dropTable($name); + } + + /** + * Returns the quoted representation of the given identifier name. + * + * Quotes non-uppercase identifiers explicitly to preserve case + * and thus make references to the particular identifier work. + * + * @param string $identifier The identifier to quote. + * + * @return string The quoted identifier. + */ + private function getQuotedIdentifierName($identifier) + { + if (preg_match('/[a-z]/', $identifier)) { + return $this->_platform->quoteIdentifier($identifier); + } + + return $identifier; + } +} 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 00000000000..5b7b82e152b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -0,0 +1,438 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Types\Type; + +/** + * PostgreSQL Schema Manager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class PostgreSqlSchemaManager extends AbstractSchemaManager +{ + /** + * @var array + */ + private $existingSchemaPaths; + + /** + * Gets 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); + } + + /** + * Returns 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 array_map('trim', $schema); + } + + /** + * Gets 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; + } + + /** + * Sets or resets the order of the existing schemas in the current search path of the user. + * + * This is a PostgreSQL only function. + * + * @return void + */ + public function determineExistingSchemaSearchPaths() + { + $names = $this->getSchemaNames(); + $paths = $this->getSchemaSearchPaths(); + + $this->existingSchemaPaths = array_filter($paths, function ($v) use ($names) { + return in_array($v, $names); + }); + } + + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + try { + parent::dropDatabase($database); + } catch (DriverException $exception) { + // If we have a SQLSTATE 55006, the drop database operation failed + // because of active connections on the database. + // To force dropping the database, we first have to close all active connections + // on that database and issue the drop database operation again. + if ($exception->getSQLState() !== '55006') { + throw $exception; + } + + $this->_execSql( + array( + $this->_platform->getDisallowDatabaseConnectionsSQL($database), + $this->_platform->getCloseActiveDatabaseConnectionsSQL($database), + ) + ); + + parent::dropDatabase($database); + } + } + + /** + * {@inheritdoc} + */ + 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) + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger['trigger_name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['schemaname'].'.'.$view['viewname'], $view['definition']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['usename'], + 'password' => $user['passwd'] + ); + } + + /** + * {@inheritdoc} + */ + 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']; + } + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + 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'], + 'where' => $row['where'], + ); + } + } + } + } + + return parent::_getPortableTableIndexesList($buffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['datname']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequencesList($sequences) + { + $sequenceDefinitions = array(); + + foreach ($sequences as $sequence) { + if ($sequence['schemaname'] != 'public') { + $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $sequenceDefinitions[$sequenceName] = $sequence; + } + + $list = array(); + + foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) { + $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]); + } + + return $list; + } + + /** + * {@inheritdoc} + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace['nspname']; + } + + /** + * {@inheritdoc} + */ + 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 ' . $this->_platform->quoteIdentifier($sequenceName)); + + return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { + // 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 (preg_match("/^'(.*)'::.*$/", $tableColumn['default'], $matches)) { + $tableColumn['default'] = $matches[1]; + } + + 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': + if ($tableColumn['default'] === 'true') { + $tableColumn['default'] = true; + } + + if ($tableColumn['default'] === 'false') { + $tableColumn['default'] = false; + } + + $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' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + ); + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['collation']) && !empty($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php new file mode 100644 index 00000000000..793a5fe8bad --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * SAP Sybase SQL Anywhere schema manager. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * Starts a database after creation + * as SQL Anywhere needs a database to be started + * before it can be used. + * + * @see startDatabase + */ + public function createDatabase($database) + { + parent::createDatabase($database); + $this->startDatabase($database); + } + + /** + * {@inheritdoc} + * + * Tries stopping a database before dropping + * as SQL Anywhere needs a database to be stopped + * before it can be dropped. + * + * @see stopDatabase + */ + public function dropDatabase($database) + { + $this->tryMethod('stopDatabase', $database); + parent::dropDatabase($database); + } + + /** + * Starts a database. + * + * @param string $database The name of the database to start. + */ + public function startDatabase($database) + { + $this->_execSql($this->_platform->getStartDatabaseSQL($database)); + } + + /** + * Stops a database. + * + * @param string $database The name of the database to stop. + */ + public function stopDatabase($database) + { + $this->_execSql($this->_platform->getStopDatabaseSQL($database)); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['start_with']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $type = $this->_platform->getDoctrineTypeMapping($tableColumn['type']); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + $precision = null; + $scale = null; + $fixed = false; + $default = null; + + if (null !== $tableColumn['default']) { + // Strip quotes from default value. + $default = preg_replace(array("/^'(.*)'$/", "/''/"), array("$1", "'"), $tableColumn['default']); + + if ('autoincrement' == $default) { + $default = null; + } + } + + switch ($tableColumn['type']) { + case 'binary': + case 'char': + case 'nchar': + $fixed = true; + } + + switch ($type) { + case 'decimal': + case 'float': + $precision = $tableColumn['length']; + $scale = $tableColumn['scale']; + } + + return new Column( + $tableColumn['column_name'], + Type::getType($type), + array( + 'length' => $type == 'string' ? $tableColumn['length'] : null, + 'precision' => $precision, + 'scale' => $scale, + 'unsigned' => (bool) $tableColumn['unsigned'], + 'fixed' => $fixed, + 'notnull' => (bool) $tableColumn['notnull'], + 'default' => $default, + 'autoincrement' => (bool) $tableColumn['autoincrement'], + 'comment' => isset($tableColumn['comment']) && '' !== $tableColumn['comment'] + ? $tableColumn['comment'] + : null, + )); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['table_name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + if (!isset($foreignKeys[$tableForeignKey['index_name']])) { + $foreignKeys[$tableForeignKey['index_name']] = array( + 'local_columns' => array($tableForeignKey['local_column']), + 'foreign_table' => $tableForeignKey['foreign_table'], + 'foreign_columns' => array($tableForeignKey['foreign_column']), + 'name' => $tableForeignKey['index_name'], + 'options' => array( + 'notnull' => $tableForeignKey['notnull'], + 'match' => $tableForeignKey['match'], + 'onUpdate' => $tableForeignKey['on_update'], + 'onDelete' => $tableForeignKey['on_delete'], + 'check_on_commit' => $tableForeignKey['check_on_commit'], + 'clustered' => $tableForeignKey['clustered'], + 'for_olap_workload' => $tableForeignKey['for_olap_workload'] + ) + ); + } else { + $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; + $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null) + { + foreach ($tableIndexRows as &$tableIndex) { + $tableIndex['primary'] = (boolean) $tableIndex['primary']; + $tableIndex['flags'] = array(); + + if ($tableIndex['clustered']) { + $tableIndex['flags'][] = 'clustered'; + } + + if ($tableIndex['with_nulls_not_distinct']) { + $tableIndex['flags'][] = 'with_nulls_not_distinct'; + } + + if ($tableIndex['for_olap_workload']) { + $tableIndex['flags'][] = 'for_olap_workload'; + } + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View( + $view['table_name'], + preg_replace('/^.*\s+as\s+SELECT(.*)/i', "SELECT$1", $view['view_def']) + ); + } +} 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 00000000000..b56f8b29179 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -0,0 +1,261 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Driver\SQLSrv\SQLSrvException; +use Doctrine\DBAL\Types\Type; + +/** + * SQL Server Schema Manager. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Juozas Kaziukenas + * @author Steve Müller + * @since 2.0 + */ +class SQLServerSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['name'], $sequence['increment'], $sequence['start_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtok($tableColumn['type'], '(), '); + $fixed = null; + $length = (int) $tableColumn['length']; + $default = $tableColumn['default']; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + while ($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) { + $default = trim($default2, "'"); + + if ($default == 'getdate()') { + $default = $this->_platform->getCurrentTimestampSQL(); + } + } + + switch ($dbType) { + case 'nchar': + case 'nvarchar': + case 'ntext': + // Unicode data requires 2 bytes per character + $length = $length / 2; + break; + case 'varchar': + // TEXT type is returned as VARCHAR(MAX) with a length of -1 + if ($length == -1) { + $dbType = 'text'; + } + break; + } + + if ('char' === $dbType || 'nchar' === $dbType || 'binary' === $dbType) { + $fixed = true; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + $options = array( + 'length' => ($length == 0 || !in_array($type, array('text', 'string'))) ? null : $length, + 'unsigned' => false, + 'fixed' => (bool) $fixed, + 'default' => $default !== 'NULL' ? $default : null, + 'notnull' => (bool) $tableColumn['notnull'], + 'scale' => $tableColumn['scale'], + 'precision' => $tableColumn['precision'], + 'autoincrement' => (bool) $tableColumn['autoincrement'], + 'comment' => $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, + ); + + $column = new Column($tableColumn['name'], Type::getType($type), $options); + + if (isset($tableColumn['collation']) && $tableColumn['collation'] !== 'NULL') { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + if ( ! isset($foreignKeys[$tableForeignKey['ForeignKey']])) { + $foreignKeys[$tableForeignKey['ForeignKey']] = array( + 'local_columns' => array($tableForeignKey['ColumnName']), + 'foreign_table' => $tableForeignKey['ReferenceTableName'], + 'foreign_columns' => array($tableForeignKey['ReferenceColumnName']), + 'name' => $tableForeignKey['ForeignKey'], + 'options' => array( + 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), + 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']) + ) + ); + } else { + $foreignKeys[$tableForeignKey['ForeignKey']]['local_columns'][] = $tableForeignKey['ColumnName']; + $foreignKeys[$tableForeignKey['ForeignKey']]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + foreach ($tableIndexRows as &$tableIndex) { + $tableIndex['non_unique'] = (boolean) $tableIndex['non_unique']; + $tableIndex['primary'] = (boolean) $tableIndex['primary']; + $tableIndex['flags'] = $tableIndex['flags'] ? array($tableIndex['flags']) : null; + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * {@inheritdoc} + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + // @todo + return new View($view['name'], null); + } + + /** + * {@inheritdoc} + */ + 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; + } + } catch (SQLSrvException $e) { + if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) { + return array(); + } else { + throw $e; + } + } + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * {@inheritdoc} + */ + 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']); + } + } + } + + parent::alterTable($tableDiff); + } + + /** + * Returns the SQL to retrieve the constraints for a given column. + * + * @param string $table + * @param string $column + * + * @return string + */ + 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 00000000000..b7e24fe9193 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php @@ -0,0 +1,510 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * 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. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Schema extends AbstractAsset +{ + /** + * The namespaces in this schema. + * + * @var array + */ + private $namespaces = array(); + + /** + * @var \Doctrine\DBAL\Schema\Table[] + */ + protected $_tables = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + protected $_sequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\SchemaConfig + */ + protected $_schemaConfig = false; + + /** + * @param \Doctrine\DBAL\Schema\Table[] $tables + * @param \Doctrine\DBAL\Schema\Sequence[] $sequences + * @param \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig + * @param array $namespaces + */ + public function __construct( + array $tables = array(), + array $sequences = array(), + SchemaConfig $schemaConfig = null, + array $namespaces = array() + ) { + if ($schemaConfig == null) { + $schemaConfig = new SchemaConfig(); + } + $this->_schemaConfig = $schemaConfig; + $this->_setName($schemaConfig->getName() ?: 'public'); + + foreach ($namespaces as $namespace) { + $this->createNamespace($namespace); + } + + foreach ($tables as $table) { + $this->_addTable($table); + } + + foreach ($sequences as $sequence) { + $this->_addSequence($sequence); + } + } + + /** + * @return boolean + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function _addTable(Table $table) + { + $namespaceName = $table->getNamespaceName(); + $tableName = $table->getFullQualifiedName($this->getName()); + + if (isset($this->_tables[$tableName])) { + throw SchemaException::tableAlreadyExists($tableName); + } + + if ( ! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) { + $this->createNamespace($namespaceName); + } + + $this->_tables[$tableName] = $table; + $table->setSchemaConfig($this->_schemaConfig); + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function _addSequence(Sequence $sequence) + { + $namespaceName = $sequence->getNamespaceName(); + $seqName = $sequence->getFullQualifiedName($this->getName()); + + if (isset($this->_sequences[$seqName])) { + throw SchemaException::sequenceAlreadyExists($seqName); + } + + if ( ! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) { + $this->createNamespace($namespaceName); + } + + $this->_sequences[$seqName] = $sequence; + } + + /** + * Returns the namespaces of this schema. + * + * @return array A list of namespace names. + */ + public function getNamespaces() + { + return $this->namespaces; + } + + /** + * Gets all tables of this schema. + * + * @return \Doctrine\DBAL\Schema\Table[] + */ + public function getTables() + { + return $this->_tables; + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + public function getTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + if (!isset($this->_tables[$tableName])) { + throw SchemaException::tableDoesNotExist($tableName); + } + + return $this->_tables[$tableName]; + } + + /** + * @param string $name + * + * @return string + */ + private function getFullQualifiedAssetName($name) + { + $name = $this->getUnquotedAssetName($name); + + if (strpos($name, ".") === false) { + $name = $this->getName() . "." . $name; + } + + return strtolower($name); + } + + /** + * Returns the unquoted representation of a given asset name. + * + * @param string $assetName Quoted or unquoted representation of an asset name. + * + * @return string + */ + private function getUnquotedAssetName($assetName) + { + if ($this->isIdentifierQuoted($assetName)) { + return $this->trimQuotes($assetName); + } + + return $assetName; + } + + /** + * Does this schema have a namespace with the given name? + * + * @param string $namespaceName + * + * @return boolean + */ + public function hasNamespace($namespaceName) + { + $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName)); + + return isset($this->namespaces[$namespaceName]); + } + + /** + * Does this schema have a table with the given name? + * + * @param string $tableName + * + * @return boolean + */ + public function hasTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + + return isset($this->_tables[$tableName]); + } + + /** + * Gets all table names, prefixed with a schema name, even the default one if present. + * + * @return array + */ + public function getTableNames() + { + return array_keys($this->_tables); + } + + /** + * @param string $sequenceName + * + * @return boolean + */ + public function hasSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + + return isset($this->_sequences[$sequenceName]); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\Sequence + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + 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; + } + + /** + * Creates a new namespace. + * + * @param string $namespaceName The name of the namespace to create. + * + * @return \Doctrine\DBAL\Schema\Schema This schema instance. + */ + public function createNamespace($namespaceName) + { + $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName)); + + if (isset($this->namespaces[$unquotedNamespaceName])) { + throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName); + } + + $this->namespaces[$unquotedNamespaceName] = $namespaceName; + + return $this; + } + + /** + * Creates a new table. + * + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + */ + public function createTable($tableName) + { + $table = new Table($tableName); + $this->_addTable($table); + + foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) { + $table->addOption($name, $value); + } + + return $table; + } + + /** + * Renames a table. + * + * @param string $oldTableName + * @param string $newTableName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function renameTable($oldTableName, $newTableName) + { + $table = $this->getTable($oldTableName); + $table->_setName($newTableName); + + $this->dropTable($oldTableName); + $this->_addTable($table); + + return $this; + } + + /** + * Drops a table from the schema. + * + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function dropTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + $this->getTable($tableName); + unset($this->_tables[$tableName]); + + return $this; + } + + /** + * Creates a new sequence. + * + * @param string $sequenceName + * @param integer $allocationSize + * @param integer $initialValue + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function createSequence($sequenceName, $allocationSize=1, $initialValue=1) + { + $seq = new Sequence($sequenceName, $allocationSize, $initialValue); + $this->_addSequence($seq); + + return $seq; + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function dropSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + unset($this->_sequences[$sequenceName]); + + return $this; + } + + /** + * Returns an array of necessary SQL queries to create the schema on the given platform. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSql(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 \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toDropSql(AbstractPlatform $platform) + { + $dropSqlCollector = new DropSchemaSqlCollector($platform); + $this->visit($dropSqlCollector); + + return $dropSqlCollector->getQueries(); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($this, $toSchema); + + return $schemaDiff->toSql($platform); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $this); + + return $schemaDiff->toSql($platform); + } + + /** + * @param \Doctrine\DBAL\Schema\Visitor\Visitor $visitor + * + * @return void + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSchema($this); + + if ($visitor instanceof NamespaceVisitor) { + foreach ($this->namespaces as $namespace) { + $visitor->acceptNamespace($namespace); + } + } + + 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 00000000000..cc23237d87b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Configuration for a Schema. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaConfig +{ + /** + * @var boolean + */ + protected $hasExplicitForeignKeyIndexes = false; + + /** + * @var integer + */ + protected $maxIdentifierLength = 63; + + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $defaultTableOptions = array(); + + /** + * @return boolean + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->hasExplicitForeignKeyIndexes; + } + + /** + * @param boolean $flag + * + * @return void + */ + public function setExplicitForeignKeyIndexes($flag) + { + $this->hasExplicitForeignKeyIndexes = (bool) $flag; + } + + /** + * @param integer $length + * + * @return void + */ + public function setMaxIdentifierLength($length) + { + $this->maxIdentifierLength = (int) $length; + } + + /** + * @return integer + */ + public function getMaxIdentifierLength() + { + return $this->maxIdentifierLength; + } + + /** + * Gets the default namespace of schema objects. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the default namespace name of schema objects. + * + * @param string $name The value to set. + * + * @return void + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the default options that are passed to Table instances created with + * Schema#createTable(). + * + * @return array + */ + public function getDefaultTableOptions() + { + return $this->defaultTableOptions; + } + + /** + * @param array $defaultTableOptions + * + * @return void + */ + public function setDefaultTableOptions(array $defaultTableOptions) + { + $this->defaultTableOptions = $defaultTableOptions; + } +} 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 00000000000..35afe1febc0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php @@ -0,0 +1,204 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Schema Diff. + * + * @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 SchemaDiff +{ + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + public $fromSchema; + + /** + * All added namespaces. + * + * @var string[] + */ + public $newNamespaces = array(); + + /** + * All removed namespaces. + * + * @var string[] + */ + public $removedNamespaces = array(); + + /** + * All added tables. + * + * @var \Doctrine\DBAL\Schema\Table[] + */ + public $newTables = array(); + + /** + * All changed tables. + * + * @var \Doctrine\DBAL\Schema\TableDiff[] + */ + public $changedTables = array(); + + /** + * All removed tables. + * + * @var \Doctrine\DBAL\Schema\Table[] + */ + public $removedTables = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $newSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $changedSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $removedSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $orphanedForeignKeys = array(); + + /** + * Constructs an SchemaDiff object. + * + * @param \Doctrine\DBAL\Schema\Table[] $newTables + * @param \Doctrine\DBAL\Schema\TableDiff[] $changedTables + * @param \Doctrine\DBAL\Schema\Table[] $removedTables + * @param \Doctrine\DBAL\Schema\Schema|null $fromSchema + */ + public function __construct($newTables = array(), $changedTables = array(), $removedTables = array(), Schema $fromSchema = null) + { + $this->newTables = $newTables; + $this->changedTables = $changedTables; + $this->removedTables = $removedTables; + $this->fromSchema = $fromSchema; + } + + /** + * 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 \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSaveSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, true); + } + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, false); + } + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @param boolean $saveMode + * + * @return array + */ + protected function _toSql(AbstractPlatform $platform, $saveMode = false) + { + $sql = array(); + + if ($platform->supportsSchemas()) { + foreach ($this->newNamespaces as $newNamespace) { + $sql[] = $platform->getCreateSchemaSQL($newNamespace); + } + } + + 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; + } +} 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 00000000000..152f374da09 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -0,0 +1,181 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +class SchemaException extends \Doctrine\DBAL\DBALException +{ + const TABLE_DOESNT_EXIST = 10; + const TABLE_ALREADY_EXISTS = 20; + const COLUMN_DOESNT_EXIST = 30; + const COLUMN_ALREADY_EXISTS = 40; + const INDEX_DOESNT_EXIST = 50; + const INDEX_ALREADY_EXISTS = 60; + const SEQUENCE_DOENST_EXIST = 70; + const SEQUENCE_ALREADY_EXISTS = 80; + const INDEX_INVALID_NAME = 90; + const FOREIGNKEY_DOESNT_EXIST = 100; + const NAMESPACE_ALREADY_EXISTS = 110; + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function tableDoesNotExist($tableName) + { + return new self("There is no table with name '".$tableName."' in the schema.", self::TABLE_DOESNT_EXIST); + } + + /** + * @param string $indexName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexNameInvalid($indexName) + { + return new self("Invalid index-name $indexName given, has to be [a-zA-Z0-9_]", self::INDEX_INVALID_NAME); + } + + /** + * @param string $indexName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexDoesNotExist($indexName, $table) + { + return new self("Index '$indexName' does not exist on table '$table'.", self::INDEX_DOESNT_EXIST); + } + + /** + * @param string $indexName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexAlreadyExists($indexName, $table) + { + return new self("An index with name '$indexName' was already defined on table '$table'.", self::INDEX_ALREADY_EXISTS); + } + + /** + * @param string $columnName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function columnDoesNotExist($columnName, $table) + { + return new self("There is no column with name '$columnName' on table '$table'.", self::COLUMN_DOESNT_EXIST); + } + + /** + * @param string $namespaceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function namespaceAlreadyExists($namespaceName) + { + return new self( + sprintf("The namespace with name '%s' already exists.", $namespaceName), + self::NAMESPACE_ALREADY_EXISTS + ); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function tableAlreadyExists($tableName) + { + return new self("The table with name '".$tableName."' already exists.", self::TABLE_ALREADY_EXISTS); + } + + /** + * @param string $tableName + * @param string $columnName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function columnAlreadyExists($tableName, $columnName) + { + return new self( + "The column '".$columnName."' on table '".$tableName."' already exists.", self::COLUMN_ALREADY_EXISTS + ); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function sequenceAlreadyExists($sequenceName) + { + return new self("The sequence '".$sequenceName."' already exists.", self::SEQUENCE_ALREADY_EXISTS); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function sequenceDoesNotExist($sequenceName) + { + return new self("There exists no sequence with the name '".$sequenceName."'.", self::SEQUENCE_DOENST_EXIST); + } + + /** + * @param string $fkName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function foreignKeyDoesNotExist($fkName, $table) + { + return new self("There exists no foreign key with the name '$fkName' on table '$table'.", self::FOREIGNKEY_DOESNT_EXIST); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function namedForeignKeyRequired(Table $localTable, ForeignKeyConstraint $foreignKey) + { + return new self( + "The performed schema operation on ".$localTable->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." + ); + } + + /** + * @param string $changeName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function alterTableChangeNotSupported($changeName) + { + return new self("Alter table change not supported, given '$changeName'"); + } +} 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 00000000000..cc5b5016e1f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php @@ -0,0 +1,166 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Sequence structure. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Sequence extends AbstractAsset +{ + /** + * @var integer + */ + protected $allocationSize = 1; + + /** + * @var integer + */ + protected $initialValue = 1; + + /** + * @var integer|null + */ + protected $cache = null; + + /** + * @param string $name + * @param integer $allocationSize + * @param integer $initialValue + * @param integer|null $cache + */ + public function __construct($name, $allocationSize = 1, $initialValue = 1, $cache = null) + { + $this->_setName($name); + $this->allocationSize = is_numeric($allocationSize) ? $allocationSize : 1; + $this->initialValue = is_numeric($initialValue) ? $initialValue : 1; + $this->cache = $cache; + } + + /** + * @return integer + */ + public function getAllocationSize() + { + return $this->allocationSize; + } + + /** + * @return integer + */ + public function getInitialValue() + { + return $this->initialValue; + } + + /** + * @return integer|null + */ + public function getCache() + { + return $this->cache; + } + + /** + * @param integer $allocationSize + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setAllocationSize($allocationSize) + { + $this->allocationSize = is_numeric($allocationSize) ? $allocationSize : 1; + + return $this; + } + + /** + * @param integer $initialValue + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setInitialValue($initialValue) + { + $this->initialValue = is_numeric($initialValue) ? $initialValue : 1; + + return $this; + } + + /** + * @param integer $cache + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setCache($cache) + { + $this->cache = $cache; + + return $this; + } + + /** + * Checks if this sequence is an autoincrement sequence for a given table. + * + * This is used inside the comparator to not report sequences as missing, + * when the "from" schema implicitly creates the sequences. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return boolean + */ + public function isAutoIncrementsFor(Table $table) + { + if ( ! $table->hasPrimaryKey()) { + return false; + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + + if (count($pkColumns) != 1) { + return false; + } + + $column = $table->getColumn($pkColumns[0]); + + if ( ! $column->getAutoincrement()) { + return false; + } + + $sequenceName = $this->getShortestName($table->getNamespaceName()); + $tableName = $table->getShortestName($table->getNamespaceName()); + $tableSequenceName = sprintf('%s_%s_seq', $tableName, $pkColumns[0]); + + return $tableSequenceName === $sequenceName; + } + + /** + * @param \Doctrine\DBAL\Schema\Visitor\Visitor $visitor + * + * @return void + */ + 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 00000000000..7cf166e3562 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -0,0 +1,433 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Types\StringType; +use Doctrine\DBAL\Types\TextType; + +/** + * Sqlite SchemaManager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Jonathan H. Wage + * @author Martin Hasoň + * @since 2.0 + */ +class SqliteSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + if (file_exists($database)) { + unlink($database); + } + } + + /** + * {@inheritdoc} + */ + 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(); + } + + /** + * {@inheritdoc} + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->fromTable = $this->listTableDetails($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->addedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->changedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropForeignKey($foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->removedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function listTableForeignKeys($table, $database = null) + { + if (null === $database) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + if ( ! empty($tableForeignKeys)) { + $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'"); + $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : ''; + if (preg_match_all('# + (?:CONSTRAINT\s+([^\s]+)\s+)? + (?:FOREIGN\s+KEY[^\)]+\)\s*)? + REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))? + (?: + [^,]*? + (NOT\s+DEFERRABLE|DEFERRABLE) + (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? + )?#isx', + $createSql, $match)) { + + $names = array_reverse($match[1]); + $deferrable = array_reverse($match[2]); + $deferred = array_reverse($match[3]); + } else { + $names = $deferrable = $deferred = array(); + } + + foreach ($tableForeignKeys as $key => $value) { + $id = $value['id']; + $tableForeignKeys[$key]['constraint_name'] = isset($names[$id]) && '' != $names[$id] ? $names[$id] : $id; + $tableForeignKeys[$key]['deferrable'] = isset($deferrable[$id]) && 'deferrable' == strtolower($deferrable[$id]) ? true : false; + $tableForeignKeys[$key]['deferred'] = isset($deferred[$id]) && 'deferred' == strtolower($deferred[$id]) ? true : false; + } + } + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + + // fetch primary + $stmt = $this->_conn->executeQuery("PRAGMA TABLE_INFO ('$tableName')"); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + usort($indexArray, function($a, $b) { + if ($a['pk'] == $b['pk']) { + return $a['cid'] - $b['cid']; + } + + return $a['pk'] - $b['pk']; + }); + foreach ($indexArray as $indexColumnRow) { + if ($indexColumnRow['pk'] != "0") { + $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); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexDefinition($tableIndex) + { + return array( + 'name' => $tableIndex['name'], + 'unique' => (bool) $tableIndex['unique'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $list = parent::_getPortableTableColumnList($table, $database, $tableColumns); + + // find column with autoincrement + $autoincrementColumn = null; + $autoincrementCount = 0; + + foreach ($tableColumns as $tableColumn) { + if ('0' != $tableColumn['pk']) { + $autoincrementCount++; + if (null === $autoincrementColumn && 'integer' == strtolower($tableColumn['type'])) { + $autoincrementColumn = $tableColumn['name']; + } + } + } + + if (1 == $autoincrementCount && null !== $autoincrementColumn) { + foreach ($list as $column) { + if ($autoincrementColumn == $column->getName()) { + $column->setAutoincrement(true); + } + } + } + + // inspect column collation + $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'"); + $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : ''; + + foreach ($list as $columnName => $column) { + $type = $column->getType(); + + if ($type instanceof StringType || $type instanceof TextType) { + $column->setPlatformOption('collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY'); + } + } + + return $list; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $parts = explode('(', $tableColumn['type']); + $tableColumn['type'] = trim($parts[0]); + if (isset($parts[1])) { + $length = trim($parts[1], ')'); + $tableColumn['length'] = $length; + } + + $dbType = strtolower($tableColumn['type']); + $length = isset($tableColumn['length']) ? $tableColumn['length'] : null; + $unsigned = false; + + if (strpos($dbType, ' unsigned') !== false) { + $dbType = str_replace(' unsigned', '', $dbType); + $unsigned = true; + } + + $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'])) { + if (strpos($tableColumn['length'], ',') === false) { + $tableColumn['length'] .= ",0"; + } + 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); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['name'], $view['sql']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = array_change_key_case($value, CASE_LOWER); + $name = $value['constraint_name']; + if ( ! isset($list[$name])) { + if ( ! isset($value['on_delete']) || $value['on_delete'] == "RESTRICT") { + $value['on_delete'] = null; + } + if ( ! isset($value['on_update']) || $value['on_update'] == "RESTRICT") { + $value['on_update'] = null; + } + + $list[$name] = array( + 'name' => $name, + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['table'], + 'onDelete' => $value['on_delete'], + 'onUpdate' => $value['on_update'], + 'deferrable' => $value['deferrable'], + 'deferred'=> $value['deferred'], + ); + } + $list[$name]['local'][] = $value['from']; + $list[$name]['foreign'][] = $value['to']; + } + + $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'], + 'deferrable' => $constraint['deferrable'], + 'deferred'=> $constraint['deferred'], + ) + ); + } + + return $result; + } + + /** + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return \Doctrine\DBAL\Schema\TableDiff + * + * @throws \Doctrine\DBAL\DBALException + */ + private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + if ( ! $table instanceof Table) { + $tableDetails = $this->tryMethod('listTableDetails', $table); + if (false === $table) { + throw new DBALException(sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table)); + } + + $table = $tableDetails; + } + + $tableDiff = new TableDiff($table->getName()); + $tableDiff->fromTable = $table; + + return $tableDiff; + } + + private function parseColumnCollationFromSQL($column, $sql) + { + if (preg_match( + '{(?:'.preg_quote($column).'|'.preg_quote($this->_platform->quoteSingleIdentifier($column)).') + [^,(]+(?:\([^()]+\)[^,]*)? + (?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)* + COLLATE\s+["\']?([^\s,"\')]+)}isx', $sql, $match)) { + return $match[1]; + } + + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php new file mode 100644 index 00000000000..3158df47de1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; + +/** + * Abstract schema synchronizer with methods for executing batches of SQL. + */ +abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer +{ + /** + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + } + + /** + * @param array $sql + */ + protected function processSqlSafely(array $sql) + { + foreach ($sql as $s) { + try { + $this->conn->exec($s); + } catch (\Exception $e) { + + } + } + } + + /** + * @param array $sql + */ + protected function processSql(array $sql) + { + foreach ($sql as $s) { + $this->conn->exec($s); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php new file mode 100644 index 00000000000..1a5552ea681 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php @@ -0,0 +1,101 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Schema\Schema; + +/** + * The synchronizer knows how to synchronize a schema with the configured + * database. + * + * @author Benjamin Eberlei + */ +interface SchemaSynchronizer +{ + /** + * Gets the SQL statements that can be executed to create the schema. + * + * @param \Doctrine\DBAL\Schema\Schema $createSchema + * + * @return array + */ + function getCreateSchema(Schema $createSchema); + + /** + * Gets the SQL Statements to update given schema with the underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param boolean $noDrops + * + * @return array + */ + function getUpdateSchema(Schema $toSchema, $noDrops = false); + + /** + * Gets the SQL Statements to drop the given schema from underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $dropSchema + * + * @return array + */ + function getDropSchema(Schema $dropSchema); + + /** + * Gets the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + function getDropAllSchema(); + + /** + * Creates the Schema. + * + * @param \Doctrine\DBAL\Schema\Schema $createSchema + * + * @return void + */ + function createSchema(Schema $createSchema); + + /** + * Updates the Schema to new schema version. + * + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param boolean $noDrops + * + * @return void + */ + function updateSchema(Schema $toSchema, $noDrops = false); + + /** + * Drops the given database schema from the underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $dropSchema + * + * @return void + */ + function dropSchema(Schema $dropSchema); + + /** + * Drops all assets from the underlying db. + * + * @return void + */ + function dropAllSchema(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php new file mode 100644 index 00000000000..7cab7a5b3a1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php @@ -0,0 +1,176 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; + +/** + * Schema Synchronizer for Default DBAL Connection. + * + * @author Benjamin Eberlei + */ +class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(Connection $conn) + { + parent::__construct($conn); + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getCreateSchema(Schema $createSchema) + { + return $createSchema->toSql($this->platform); + } + + + /** + * {@inheritdoc} + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + $comparator = new Comparator(); + $sm = $this->conn->getSchemaManager(); + + $fromSchema = $sm->createSchema(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($noDrops) { + return $schemaDiff->toSaveSql($this->platform); + } + + return $schemaDiff->toSql($this->platform); + } + + /** + * {@inheritdoc} + */ + public function getDropSchema(Schema $dropSchema) + { + $visitor = new DropSchemaSqlCollector($this->platform); + $sm = $this->conn->getSchemaManager(); + + $fullSchema = $sm->createSchema(); + + foreach ($fullSchema->getTables() as $table) { + if ($dropSchema->hasTable($table->getName())) { + $visitor->acceptTable($table); + } + + foreach ($table->getForeignKeys() as $foreignKey) { + if ( ! $dropSchema->hasTable($table->getName())) { + continue; + } + + if ( ! $dropSchema->hasTable($foreignKey->getForeignTableName())) { + continue; + } + + $visitor->acceptForeignKey($table, $foreignKey); + } + } + + if ( ! $this->platform->supportsSequences()) { + return $visitor->getQueries(); + } + + foreach ($dropSchema->getSequences() as $sequence) { + $visitor->acceptSequence($sequence); + } + + foreach ($dropSchema->getTables() as $table) { + if ( ! $table->hasPrimaryKey()) { + continue; + } + + $columns = $table->getPrimaryKey()->getColumns(); + if (count($columns) > 1) { + continue; + } + + $checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; + if ($fullSchema->hasSequence($checkSequence)) { + $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); + } + } + + return $visitor->getQueries(); + } + + /** + * {@inheritdoc} + */ + public function getDropAllSchema() + { + $sm = $this->conn->getSchemaManager(); + $visitor = new DropSchemaSqlCollector($this->platform); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $sm->createSchema(); + $schema->visit($visitor); + + return $visitor->getQueries(); + } + + /** + * {@inheritdoc} + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * {@inheritdoc} + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * {@inheritdoc} + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * {@inheritdoc} + */ + public function dropAllSchema() + { + $this->processSql($this->getDropAllSchema()); + } +} 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 00000000000..857caeb0859 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php @@ -0,0 +1,850 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\DBALException; + +/** + * Object Representation of a table. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Table extends AbstractAsset +{ + /** + * @var string + */ + protected $_name = null; + + /** + * @var Column[] + */ + protected $_columns = array(); + + /** + * @var Index[] + */ + private $implicitIndexes = array(); + + /** + * @var Index[] + */ + protected $_indexes = array(); + + /** + * @var string + */ + protected $_primaryKeyName = false; + + /** + * @var ForeignKeyConstraint[] + */ + protected $_fkConstraints = array(); + + /** + * @var array + */ + protected $_options = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = null; + + /** + * @param string $tableName + * @param Column[] $columns + * @param Index[] $indexes + * @param ForeignKeyConstraint[] $fkConstraints + * @param integer $idGeneratorType + * @param array $options + * + * @throws DBALException + */ + 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); + + 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 + * + * @return void + */ + public function setSchemaConfig(SchemaConfig $schemaConfig) + { + $this->_schemaConfig = $schemaConfig; + } + + /** + * @return integer + */ + protected function _getMaxIdentifierLength() + { + if ($this->_schemaConfig instanceof SchemaConfig) { + return $this->_schemaConfig->getMaxIdentifierLength(); + } else { + return 63; + } + } + + /** + * Sets the Primary Key. + * + * @param array $columns + * @param string|boolean $indexName + * + * @return self + */ + public function setPrimaryKey(array $columns, $indexName = false) + { + $this->_addIndex($this->_createIndex($columns, $indexName ?: "primary", true, true)); + + foreach ($columns as $columnName) { + $column = $this->getColumn($columnName); + $column->setNotnull(true); + } + + return $this; + } + + /** + * @param array $columnNames + * @param string|null $indexName + * @param array $flags + * @param array $options + * + * @return self + */ + public function addIndex(array $columnNames, $indexName = null, array $flags = array(), array $options = array()) + { + if ($indexName == null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); + } + + /** + * Drops the primary key from this table. + * + * @return void + */ + public function dropPrimaryKey() + { + $this->dropIndex($this->_primaryKeyName); + $this->_primaryKeyName = false; + } + + /** + * Drops an index from this table. + * + * @param string $indexName The index name. + * + * @return void + * + * @throws SchemaException If the index does not exist. + */ + public function dropIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + unset($this->_indexes[$indexName]); + } + + /** + * @param array $columnNames + * @param string|null $indexName + * @param array $options + * + * @return self + */ + public function addUniqueIndex(array $columnNames, $indexName = null, array $options = array()) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, array(), $options)); + } + + /** + * Renames an index. + * + * @param string $oldIndexName The name of the index to rename from. + * @param string|null $newIndexName The name of the index to rename to. + * If null is given, the index name will be auto-generated. + * + * @return self This table instance. + * + * @throws SchemaException if no index exists for the given current name + * or if an index with the given new name already exists on this table. + */ + public function renameIndex($oldIndexName, $newIndexName = null) + { + $oldIndexName = $this->normalizeIdentifier($oldIndexName); + $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName); + + if ($oldIndexName === $normalizedNewIndexName) { + return $this; + } + + if ( ! $this->hasIndex($oldIndexName)) { + throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name); + } + + if ($this->hasIndex($normalizedNewIndexName)) { + throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name); + } + + $oldIndex = $this->_indexes[$oldIndexName]; + + if ($oldIndex->isPrimary()) { + $this->dropPrimaryKey(); + + return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName); + } + + unset($this->_indexes[$oldIndexName]); + + if ($oldIndex->isUnique()) { + return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName); + } + + return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags()); + } + + /** + * Checks if an index begins in the order of the given columns. + * + * @param array $columnsNames + * + * @return boolean + */ + 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 boolean $isUnique + * @param boolean $isPrimary + * @param array $flags + * @param array $options + * + * @return Index + * + * @throws SchemaException + */ + private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = array(), array $options = array()) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($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); + } + } + + return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); + } + + /** + * @param string $columnName + * @param string $typeName + * @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; + } + + /** + * Renames a Column. + * + * @param string $oldColumnName + * @param string $newColumnName + * + * @return self + * + * @throws DBALException + */ + public function renameColumn($oldColumnName, $newColumnName) + { + throw new DBALException("Table#renameColumn() was removed, because it drops and recreates " . + "the column instead. There is no fix available, because a schema diff cannot reliably detect if a " . + "column was renamed or one column was created and another one dropped."); + } + + /** + * Change Column Details. + * + * @param string $columnName + * @param array $options + * + * @return self + */ + public function changeColumn($columnName, array $options) + { + $column = $this->getColumn($columnName); + $column->setOptions($options); + + return $this; + } + + /** + * Drops a Column from the Table. + * + * @param string $columnName + * + * @return self + */ + public function dropColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + unset($this->_columns[$columnName]); + + return $this; + } + + /** + * Adds a foreign key constraint. + * + * Name is inferred from the local columns. + * + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * @param string|null $constraintName + * + * @return self + */ + 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); + } + + /** + * Adds a foreign key constraint. + * + * Name is to be generated by the database itself. + * + * @deprecated Use {@link addForeignKeyConstraint} + * + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * + * @return self + */ + public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Adds a foreign key constraint with a given name. + * + * @deprecated Use {@link addForeignKeyConstraint} + * + * @param string $name + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * + * @return self + * + * @throws SchemaException + */ + public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + if ($foreignTable instanceof Table) { + foreach ($foreignColumnNames as $columnName) { + if ( ! $foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); + } + } + } + + foreach ($localColumnNames as $columnName) { + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + $constraint = new ForeignKeyConstraint( + $localColumnNames, $foreignTable, $foreignColumnNames, $name, $options + ); + $this->_addForeignKeyConstraint($constraint); + + return $this; + } + + /** + * @param string $name + * @param string $value + * + * @return self + */ + public function addOption($name, $value) + { + $this->_options[$name] = $value; + + return $this; + } + + /** + * @param Column $column + * + * @return void + * + * @throws SchemaException + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = $this->normalizeIdentifier($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Adds an index to the table. + * + * @param Index $indexCandidate + * + * @return self + * + * @throws SchemaException + */ + protected function _addIndex(Index $indexCandidate) + { + $indexName = $indexCandidate->getName(); + $indexName = $this->normalizeIdentifier($indexName); + $replacedImplicitIndexes = array(); + + foreach ($this->implicitIndexes as $name => $implicitIndex) { + if ($implicitIndex->isFullfilledBy($indexCandidate) && isset($this->_indexes[$name])) { + $replacedImplicitIndexes[] = $name; + } + } + + if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || + ($this->_primaryKeyName != false && $indexCandidate->isPrimary()) + ) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + foreach ($replacedImplicitIndexes as $name) { + unset($this->_indexes[$name], $this->implicitIndexes[$name]); + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + + return $this; + } + + /** + * @param ForeignKeyConstraint $constraint + * + * @return void + */ + 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 = $this->normalizeIdentifier($name); + + $this->_fkConstraints[$name] = $constraint; + + // add an explicit index on the foreign key columns. If there is already an index that fulfils 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 computation overhead in this case, however no duplicate indexes are ever added (based on columns). + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $constraint->getColumns()), + "idx", + $this->_getMaxIdentifierLength() + ); + $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return; + } + } + + $this->_addIndex($indexCandidate); + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + } + + /** + * Returns whether this table has a foreign key constraint with the given name. + * + * @param string $constraintName + * + * @return boolean + */ + public function hasForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + + return isset($this->_fkConstraints[$constraintName]); + } + + /** + * Returns the foreign key constraint with the given name. + * + * @param string $constraintName The constraint name. + * + * @return ForeignKeyConstraint + * + * @throws SchemaException If the foreign key does not exist. + */ + public function getForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + if (!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + return $this->_fkConstraints[$constraintName]; + } + + /** + * Removes the foreign key constraint with the given name. + * + * @param string $constraintName The constraint name. + * + * @return void + * + * @throws SchemaException + */ + public function removeForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($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; + } + + /** + * Returns whether this table has a Column with the given name. + * + * @param string $columnName The column name. + * + * @return boolean + */ + public function hasColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + + return isset($this->_columns[$columnName]); + } + + /** + * Returns the Column with the given name. + * + * @param string $columnName The column name. + * + * @return Column + * + * @throws SchemaException If the column does not exist. + */ + public function getColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + + return $this->_columns[$columnName]; + } + + /** + * Returns the primary key. + * + * @return Index|null The primary key, or null if this Table has no primary key. + */ + public function getPrimaryKey() + { + if ( ! $this->hasPrimaryKey()) { + return null; + } + + return $this->getIndex($this->_primaryKeyName); + } + + /** + * Returns the primary key columns. + * + * @return array + * + * @throws DBALException + */ + public function getPrimaryKeyColumns() + { + if ( ! $this->hasPrimaryKey()) { + throw new DBALException("Table " . $this->getName() . " has no primary key."); + } + + return $this->getPrimaryKey()->getColumns(); + } + + /** + * Returns whether this table has a primary key. + * + * @return boolean + */ + public function hasPrimaryKey() + { + return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName)); + } + + /** + * Returns whether this table has an Index with the given name. + * + * @param string $indexName The index name. + * + * @return boolean + */ + public function hasIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + + return (isset($this->_indexes[$indexName])); + } + + /** + * Returns the Index with the given name. + * + * @param string $indexName The index name. + * + * @return Index + * + * @throws SchemaException If the index does not exist. + */ + public function getIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + + return $this->_indexes[$indexName]; + } + + /** + * @return Index[] + */ + public function getIndexes() + { + return $this->_indexes; + } + + /** + * Returns the foreign key constraints. + * + * @return ForeignKeyConstraint[] + */ + public function getForeignKeys() + { + return $this->_fkConstraints; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @param Visitor $visitor + * + * @return void + */ + 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. + * + * @return void + */ + 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); + } + } + + /** + * Normalizes a given identifier. + * + * Trims quotes and lowercases the given identifier. + * + * @param string $identifier The identifier to normalize. + * + * @return string The normalized identifier. + */ + private function normalizeIdentifier($identifier) + { + return $this->trimQuotes(strtolower($identifier)); + } +} 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 00000000000..a837fab6746 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php @@ -0,0 +1,170 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Table Diff. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class TableDiff +{ + /** + * @var string + */ + public $name = null; + + /** + * @var string|boolean + */ + public $newName = false; + + /** + * All added fields. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $addedColumns; + + /** + * All changed fields. + * + * @var \Doctrine\DBAL\Schema\ColumnDiff[] + */ + public $changedColumns = array(); + + /** + * All removed fields. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $removedColumns = array(); + + /** + * Columns that are only renamed from key to column instance name. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $renamedColumns = array(); + + /** + * All added indexes. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $addedIndexes = array(); + + /** + * All changed indexes. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $changedIndexes = array(); + + /** + * All removed indexes + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $removedIndexes = array(); + + /** + * Indexes that are only renamed but are identical otherwise. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $renamedIndexes = array(); + + /** + * All added foreign key definitions + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $addedForeignKeys = array(); + + /** + * All changed foreign keys + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $changedForeignKeys = array(); + + /** + * All removed foreign keys + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $removedForeignKeys = array(); + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + public $fromTable; + + /** + * Constructs an TableDiff object. + * + * @param string $tableName + * @param \Doctrine\DBAL\Schema\Column[] $addedColumns + * @param \Doctrine\DBAL\Schema\ColumnDiff[] $changedColumns + * @param \Doctrine\DBAL\Schema\Column[] $removedColumns + * @param \Doctrine\DBAL\Schema\Index[] $addedIndexes + * @param \Doctrine\DBAL\Schema\Index[] $changedIndexes + * @param \Doctrine\DBAL\Schema\Index[] $removedIndexes + * @param \Doctrine\DBAL\Schema\Table|null $fromTable + */ + public function __construct($tableName, $addedColumns = array(), + $changedColumns = array(), $removedColumns = array(), $addedIndexes = array(), + $changedIndexes = array(), $removedIndexes = array(), Table $fromTable = null) + { + $this->name = $tableName; + $this->addedColumns = $addedColumns; + $this->changedColumns = $changedColumns; + $this->removedColumns = $removedColumns; + $this->addedIndexes = $addedIndexes; + $this->changedIndexes = $changedIndexes; + $this->removedIndexes = $removedIndexes; + $this->fromTable = $fromTable; + } + + /** + * @param AbstractPlatform $platform The platform to use for retrieving this table diff's name. + * + * @return \Doctrine\DBAL\Schema\Identifier + */ + public function getName(AbstractPlatform $platform) + { + return new Identifier( + $this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name + ); + } + + /** + * @return \Doctrine\DBAL\Schema\Identifier|boolean + */ + public function getNewName() + { + return $this->newName ? new Identifier($this->newName) : $this->newName; + } +} 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 00000000000..0ef7d305bbb --- /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. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class View extends AbstractAsset +{ + /** + * @var string + */ + private $_sql; + + /** + * @param string $name + * @param string $sql + */ + public function __construct($name, $sql) + { + $this->_setName($name); + $this->_sql = $sql; + } + + /** + * @return string + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php new file mode 100644 index 00000000000..7bf5b2fec32 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php @@ -0,0 +1,85 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Abstract Visitor with empty methods for easy extension. + */ +class AbstractVisitor implements Visitor, NamespaceVisitor +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptNamespace($namespaceName) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + public function acceptTable(Table $table) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + } +} 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 00000000000..5e0c6ed437e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +class CreateSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var array + */ + private $createNamespaceQueries = array(); + + /** + * @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; + } + + /** + * {@inheritdoc} + */ + public function acceptNamespace($namespaceName) + { + if ($this->platform->supportsSchemas()) { + $this->createNamespaceQueries = array_merge( + $this->createNamespaceQueries, + (array) $this->platform->getCreateSchemaSQL($namespaceName) + ); + } + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->createTableQueries = array_merge($this->createTableQueries, (array) $this->platform->getCreateTableSQL($table)); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if ($this->platform->supportsForeignKeyConstraints()) { + $this->createFkConstraintQueries = array_merge( + $this->createFkConstraintQueries, + (array) $this->platform->getCreateForeignKeySQL( + $fkConstraint, $localTable + ) + ); + } + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + $this->createSequenceQueries = array_merge( + $this->createSequenceQueries, + (array) $this->platform->getCreateSequenceSQL($sequence) + ); + } + + /** + * @return void + */ + public function resetQueries() + { + $this->createNamespaceQueries = array(); + $this->createTableQueries = array(); + $this->createSequenceQueries = array(); + $this->createFkConstraintQueries = array(); + } + + /** + * Gets all queries collected so far. + * + * @return array + */ + public function getQueries() + { + $sql = array(); + + foreach ($this->createNamespaceQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + foreach ($this->createTableQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + foreach ($this->createSequenceQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + foreach ($this->createFkConstraintQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + return $sql; + } +} 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 00000000000..ab9752d5842 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\SchemaException; + +/** + * Gathers SQL statements that allow to completely drop the current schema. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DropSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var \SplObjectStorage + */ + private $constraints; + + /** + * @var \SplObjectStorage + */ + private $sequences; + + /** + * @var \SplObjectStorage + */ + private $tables; + + /** + * @var AbstractPlatform + */ + private $platform; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + $this->clearQueries(); + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->tables->attach($table); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if (strlen($fkConstraint->getName()) == 0) { + throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); + } + + $this->constraints->attach($fkConstraint, $localTable); + } + + /** + * {@inheritdoc} + */ + 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 00000000000..1573ddf5b22 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php @@ -0,0 +1,174 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; + +/** + * Create a Graphviz output of a Schema. + */ +class Graphviz extends AbstractVisitor +{ + /** + * @var string + */ + private $output = ''; + + /** + * {@inheritdoc} + */ + 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', + ) + ); + } + + /** + * {@inheritdoc} + */ + 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"; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->output .= $this->createNode( + $table->getName(), + array( + 'label' => $this->createTableLabel($table), + 'shape' => 'plaintext', + ) + ); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return string + */ + 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; + } + + /** + * @param string $name + * @param array $options + * + * @return string + */ + private function createNode($name, $options) + { + $node = $name . " ["; + foreach ($options as $key => $value) { + $node .= $key . '=' . $value . ' '; + } + $node .= "]\n"; + + return $node; + } + + /** + * @param string $node1 + * @param string $node2 + * @param array $options + * + * @return string + */ + private function createNodeRelation($node1, $node2, $options) + { + $relation = $node1 . ' -> ' . $node2 . ' ['; + foreach ($options as $key => $value) { + $relation .= $key . '=' . $value . ' '; + } + $relation .= "]\n"; + + return $relation; + } + + /** + * Get Graphviz Output + * + * @return string + */ + public function getOutput() + { + return $this->output . "}"; + } + + /** + * Writes 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->getOutput()); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php new file mode 100644 index 00000000000..da7b1edbcaa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +/** + * Visitor that can visit schema namespaces. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface NamespaceVisitor +{ + /** + * Accepts a schema namespace name. + * + * @param string $namespaceName The schema namespace name to accept. + */ + public function acceptNamespace($namespaceName); +} 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 00000000000..e328d071cde --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +/** + * Removes 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 extends AbstractVisitor +{ + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $this->schema = $schema; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + if ( ! $table->isInDefaultNamespace($this->schema->getName())) { + $this->schema->dropTable($table->getName()); + } + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + if ( ! $sequence->isInDefaultNamespace($this->schema->getName())) { + $this->schema->dropSequence($sequence->getName()); + } + } + + /** + * {@inheritdoc} + */ + 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()); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php new file mode 100644 index 00000000000..bde4453b357 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +/** + * Visit a SchemaDiff. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Benjamin Eberlei + */ +interface SchemaDiffVisitor +{ + /** + * Visit an orphaned foreign key whose table was deleted. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + */ + function visitOrphanedForeignKey(ForeignKeyConstraint $foreignKey); + + /** + * Visit a sequence that has changed. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitChangedSequence(Sequence $sequence); + + /** + * Visit a sequence that has been removed. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitRemovedSequence(Sequence $sequence); + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitNewSequence(Sequence $sequence); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + function visitNewTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + */ + function visitNewTableForeignKey(Table $table, ForeignKeyConstraint $foreignKey); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + function visitRemovedTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + */ + function visitChangedTable(TableDiff $tableDiff); +} 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 00000000000..656f0e20c6f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Schema Visitor used for Validation or Generation purposes. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +interface Visitor +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * + * @return void + */ + public function acceptSchema(Schema $schema); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function acceptTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return void + */ + public function acceptColumn(Table $table, Column $column); + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $fkConstraint + * + * @return void + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return void + */ + public function acceptIndex(Table $table, Index $index); + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + */ + public function acceptSequence(Sequence $sequence); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php new file mode 100644 index 00000000000..c5fb77681a6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php @@ -0,0 +1,272 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Sharding implementation that pools many different connections + * internally and serves data from the currently active connection. + * + * The internals of this class are: + * + * - All sharding clients are specified and given a shard-id during + * configuration. + * - By default, the global shard is selected. If no global shard is configured + * an exception is thrown on access. + * - Selecting a shard by distribution value delegates the mapping + * "distributionValue" => "client" to the ShardChooser interface. + * - An exception is thrown if trying to switch shards during an open + * transaction. + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Sharding\PoolingShardConnection', + * 'driver' => 'pdo_mysql', + * 'global' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'shards' => array( + * array('id' => 1, 'user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('id' => 2, 'user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ), + * 'shardChoser' => 'Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser', + * )); + * $shardManager = $conn->getShardManager(); + * $shardManager->selectGlobal(); + * $shardManager->selectShard($value); + * + * @author Benjamin Eberlei + */ +class PoolingShardConnection extends Connection +{ + /** + * @var array + */ + private $activeConnections; + + /** + * @var integer + */ + private $activeShardId; + + /** + * @var array + */ + private $connections; + + /** + * @param array $params + * @param \Doctrine\DBAL\Driver $driver + * @param \Doctrine\DBAL\Configuration $config + * @param \Doctrine\Common\EventManager $eventManager + * + * @throws \InvalidArgumentException + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['global']) || !isset($params['shards'])) { + throw new \InvalidArgumentException("Connection Parameters require 'global' and 'shards' configurations."); + } + + if ( !isset($params['shardChoser'])) { + throw new \InvalidArgumentException("Missing Shard Choser configuration 'shardChoser'"); + } + + if (is_string($params['shardChoser'])) { + $params['shardChoser'] = new $params['shardChoser']; + } + + if ( ! ($params['shardChoser'] instanceof ShardChoser)) { + throw new \InvalidArgumentException("The 'shardChoser' configuration is not a valid instance of Doctrine\DBAL\Sharding\ShardChoser\ShardChoser"); + } + + $this->connections[0] = array_merge($params, $params['global']); + + foreach ($params['shards'] as $shard) { + if ( ! isset($shard['id'])) { + throw new \InvalidArgumentException("Missing 'id' for one configured shard. Please specify a unique shard-id."); + } + + if ( !is_numeric($shard['id']) || $shard['id'] < 1) { + throw new \InvalidArgumentException("Shard Id has to be a non-negative number."); + } + + if (isset($this->connections[$shard['id']])) { + throw new \InvalidArgumentException("Shard " . $shard['id'] . " is duplicated in the configuration."); + } + + $this->connections[$shard['id']] = array_merge($params, $shard); + } + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Get active shard id. + * + * @return integer + */ + public function getActiveShardId() + { + return $this->activeShardId; + } + + /** + * {@inheritdoc} + */ + public function getParams() + { + return $this->activeShardId ? $this->connections[$this->activeShardId] : $this->connections[0]; + } + + /** + * {@inheritdoc} + */ + public function getHost() + { + $params = $this->getParams(); + + return isset($params['host']) ? $params['host'] : parent::getHost(); + } + + /** + * {@inheritdoc} + */ + public function getPort() + { + $params = $this->getParams(); + + return isset($params['port']) ? $params['port'] : parent::getPort(); + } + + /** + * {@inheritdoc} + */ + public function getUsername() + { + $params = $this->getParams(); + + return isset($params['user']) ? $params['user'] : parent::getUsername(); + } + + /** + * {@inheritdoc} + */ + public function getPassword() + { + $params = $this->getParams(); + + return isset($params['password']) ? $params['password'] : parent::getPassword(); + } + + /** + * Connects to a given shard. + * + * @param mixed $shardId + * + * @return boolean + * + * @throws \Doctrine\DBAL\Sharding\ShardingException + */ + public function connect($shardId = null) + { + if ($shardId === null && $this->_conn) { + return false; + } + + if ($shardId !== null && $shardId === $this->activeShardId) { + return false; + } + + if ($this->getTransactionNestingLevel() > 0) { + throw new ShardingException("Cannot switch shard when transaction is active."); + } + + $this->activeShardId = (int)$shardId; + + if (isset($this->activeConnections[$this->activeShardId])) { + $this->_conn = $this->activeConnections[$this->activeShardId]; + + return false; + } + + $this->_conn = $this->activeConnections[$this->activeShardId] = $this->connectTo($this->activeShardId); + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connects to a specific connection. + * + * @param string $shardId + * + * @return \Doctrine\DBAL\Driver\Connection + */ + protected function connectTo($shardId) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array(); + + $connectionParams = $this->connections[$shardId]; + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + /** + * @param string|null $shardId + * + * @return boolean + */ + public function isConnected($shardId = null) + { + if ($shardId === null) { + return $this->_conn !== null; + } + + return isset($this->activeConnections[$shardId]); + } + + /** + * @return void + */ + public function close() + { + $this->_conn = null; + $this->activeConnections = null; + $this->activeShardId = null; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php new file mode 100644 index 00000000000..64307ca6664 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Shard Manager for the Connection Pooling Shard Strategy + * + * @author Benjamin Eberlei + */ +class PoolingShardManager implements ShardManager +{ + /** + * @var PoolingShardConnection + */ + private $conn; + + /** + * @var ShardChoser + */ + private $choser; + + /** + * @var string|null + */ + private $currentDistributionValue; + + /** + * @param PoolingShardConnection $conn + */ + public function __construct(PoolingShardConnection $conn) + { + $params = $conn->getParams(); + $this->conn = $conn; + $this->choser = $params['shardChoser']; + } + + /** + * @return void + */ + public function selectGlobal() + { + $this->conn->connect(0); + $this->currentDistributionValue = null; + } + + /** + * @param string $distributionValue + * + * @return void + */ + public function selectShard($distributionValue) + { + $shardId = $this->choser->pickShard($distributionValue, $this->conn); + $this->conn->connect($shardId); + $this->currentDistributionValue = $distributionValue; + } + + /** + * @return string|null + */ + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + /** + * @return array + */ + public function getShards() + { + $params = $this->conn->getParams(); + $shards = array(); + + foreach ($params['shards'] as $shard) { + $shards[] = array('id' => $shard['id']); + } + + return $shards; + } + + /** + * @param string $sql + * @param array $params + * @param array $types + * + * @return array + * + * @throws \RuntimeException + */ + public function queryAll($sql, array $params, array $types) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found."); + } + + $result = array(); + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->conn->connect($shard['id']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php new file mode 100644 index 00000000000..edda835876b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php @@ -0,0 +1,298 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +use Doctrine\DBAL\Schema\Synchronizer\AbstractSchemaSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer; + +/** + * SQL Azure Schema Synchronizer. + * + * Will iterate over all shards when performing schema operations. This is done + * by partitioning the passed schema into subschemas for the federation and the + * global database and then applying the operations step by step using the + * {@see \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer}. + * + * @author Benjamin Eberlei + */ +class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer +{ + const FEDERATION_TABLE_FEDERATED = 'azure.federated'; + const FEDERATION_DISTRIBUTION_NAME = 'azure.federatedOnDistributionName'; + + /** + * @var \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager + */ + private $shardManager; + + /** + * @var \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer + */ + private $synchronizer; + + /** + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager $shardManager + * @param \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer|null $sync + */ + public function __construct(Connection $conn, SQLAzureShardManager $shardManager, SchemaSynchronizer $sync = null) + { + parent::__construct($conn); + $this->shardManager = $shardManager; + $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn); + } + + /** + * {@inheritdoc} + */ + public function getCreateSchema(Schema $createSchema) + { + $sql = array(); + + list($global, $federation) = $this->partitionSchema($createSchema); + + $globalSql = $this->synchronizer->getCreateSchema($global); + if ($globalSql) { + $sql[] = "-- Create Root Federation\n" . + "USE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $federationSql = $this->synchronizer->getCreateSchema($federation); + + if ($federationSql) { + $defaultValue = $this->getFederationTypeDefaultValue(); + + $sql[] = $this->getCreateFederationStatement(); + $sql[] = "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $defaultValue . ") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + return $this->work($toSchema, function ($synchronizer, $schema) use ($noDrops) { + return $synchronizer->getUpdateSchema($schema, $noDrops); + }); + } + + /** + * {@inheritdoc} + */ + public function getDropSchema(Schema $dropSchema) + { + return $this->work($dropSchema, function ($synchronizer, $schema) { + return $synchronizer->getDropSchema($schema); + }); + } + + /** + * {@inheritdoc} + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * {@inheritdoc} + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * {@inheritdoc} + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * {@inheritdoc} + */ + public function getDropAllSchema() + { + $this->shardManager->selectGlobal(); + $globalSql = $this->synchronizer->getDropAllSchema(); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $this->synchronizer->getDropAllSchema(); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + $sql[] = "USE FEDERATION ROOT WITH RESET;"; + $sql[] = "DROP FEDERATION " . $this->shardManager->getFederationName(); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function dropAllSchema() + { + $this->processSqlSafely($this->getDropAllSchema()); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * + * @return array + */ + private function partitionSchema(Schema $schema) + { + return array( + $this->extractSchemaFederation($schema, false), + $this->extractSchemaFederation($schema, true), + ); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param boolean $isFederation + * + * @return \Doctrine\DBAL\Schema\Schema + * + * @throws \RuntimeException + */ + private function extractSchemaFederation(Schema $schema, $isFederation) + { + $partitionedSchema = clone $schema; + + foreach ($partitionedSchema->getTables() as $table) { + if ($isFederation) { + $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey()); + } + + if ($table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + $partitionedSchema->dropTable($table->getName()); + } else { + foreach ($table->getForeignKeys() as $fk) { + $foreignTable = $schema->getTable($fk->getForeignTableName()); + if ($foreignTable->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + throw new \RuntimeException("Cannot have foreign key between global/federation."); + } + } + } + } + + return $partitionedSchema; + } + + /** + * Work on the Global/Federation based on currently existing shards and + * perform the given operation on the underlying schema synchronizer given + * the different partitioned schema instances. + * + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param \Closure $operation + * + * @return array + */ + private function work(Schema $schema, \Closure $operation) + { + list($global, $federation) = $this->partitionSchema($schema); + $sql = array(); + + $this->shardManager->selectGlobal(); + $globalSql = $operation($this->synchronizer, $global); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $operation($this->synchronizer, $federation); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + return $sql; + } + + /** + * @return string + */ + private function getFederationTypeDefaultValue() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + + switch ($federationType->getName()) { + case Type::GUID: + $defaultValue = '00000000-0000-0000-0000-000000000000'; + break; + case Type::INTEGER: + case Type::SMALLINT: + case Type::BIGINT: + $defaultValue = '0'; + break; + default: + $defaultValue = ''; + break; + } + + return $defaultValue; + } + + /** + * @return string + */ + private function getCreateFederationStatement() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + $federationTypeSql = $federationType->getSqlDeclaration(array(), $this->conn->getDatabasePlatform()); + + return "--Create Federation\n" . + "CREATE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " " . $federationTypeSql ." RANGE)"; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php new file mode 100644 index 00000000000..b9047a9563c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Sharding\ShardManager; +use Doctrine\DBAL\Sharding\ShardingException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Sharding using the SQL Azure Federations support. + * + * @author Benjamin Eberlei + */ +class SQLAzureShardManager implements ShardManager +{ + /** + * @var string + */ + private $federationName; + + /** + * @var boolean + */ + private $filteringEnabled; + + /** + * @var string + */ + private $distributionKey; + + /** + * @var string + */ + private $distributionType; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var string + */ + private $currentDistributionValue; + + /** + * @param \Doctrine\DBAL\Connection $conn + * + * @throws \Doctrine\DBAL\Sharding\ShardingException + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + $params = $conn->getParams(); + + if ( ! isset($params['sharding']['federationName'])) { + throw ShardingException::missingDefaultFederationName(); + } + + if ( ! isset($params['sharding']['distributionKey'])) { + throw ShardingException::missingDefaultDistributionKey(); + } + + if ( ! isset($params['sharding']['distributionType'])) { + throw ShardingException::missingDistributionType(); + } + + $this->federationName = $params['sharding']['federationName']; + $this->distributionKey = $params['sharding']['distributionKey']; + $this->distributionType = $params['sharding']['distributionType']; + $this->filteringEnabled = (isset($params['sharding']['filteringEnabled'])) ? (bool) $params['sharding']['filteringEnabled'] : false; + } + + /** + * Gets the name of the federation. + * + * @return string + */ + public function getFederationName() + { + return $this->federationName; + } + + /** + * Gets the distribution key. + * + * @return string + */ + public function getDistributionKey() + { + return $this->distributionKey; + } + + /** + * Gets the Doctrine Type name used for the distribution. + * + * @return string + */ + public function getDistributionType() + { + return $this->distributionType; + } + + /** + * Sets Enabled/Disable filtering on the fly. + * + * @param boolean $flag + * + * @return void + */ + public function setFilteringEnabled($flag) + { + $this->filteringEnabled = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function selectGlobal() + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + $sql = "USE FEDERATION ROOT WITH RESET"; + $this->conn->exec($sql); + $this->currentDistributionValue = null; + } + + /** + * {@inheritDoc} + */ + public function selectShard($distributionValue) + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + if ($distributionValue === null || is_bool($distributionValue) || !is_scalar($distributionValue)) { + throw ShardingException::noShardDistributionValue(); + } + + $platform = $this->conn->getDatabasePlatform(); + $sql = sprintf( + "USE FEDERATION %s (%s = %s) WITH RESET, FILTERING = %s;", + $platform->quoteIdentifier($this->federationName), + $platform->quoteIdentifier($this->distributionKey), + $this->conn->quote($distributionValue), + ($this->filteringEnabled ? 'ON' : 'OFF') + ); + + $this->conn->exec($sql); + $this->currentDistributionValue = $distributionValue; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + /** + * {@inheritDoc} + */ + public function getShards() + { + $sql = "SELECT member_id as id, + distribution_name as distribution_key, + CAST(range_low AS CHAR) AS rangeLow, + CAST(range_high AS CHAR) AS rangeHigh + FROM sys.federation_member_distributions d + INNER JOIN sys.federations f ON f.federation_id = d.federation_id + WHERE f.name = " . $this->conn->quote($this->federationName); + + return $this->conn->fetchAll($sql); + } + + /** + * {@inheritDoc} + */ + public function queryAll($sql, array $params = array(), array $types = array()) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found for " . $this->federationName); + } + + $result = array(); + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->selectShard($shard['rangeLow']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } + + /** + * Splits Federation at a given distribution value. + * + * @param mixed $splitDistributionValue + * + * @return void + */ + public function splitFederation($splitDistributionValue) + { + $type = Type::getType($this->distributionType); + + $sql = "ALTER FEDERATION " . $this->getFederationName() . " " . + "SPLIT AT (" . $this->getDistributionKey() . " = " . + $this->conn->quote($splitDistributionValue, $type->getBindingType()) . ")"; + $this->conn->exec($sql); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php new file mode 100644 index 00000000000..c1a524f76a4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Converts a single tenant schema into a multi-tenant schema for SQL Azure + * Federations under the following assumptions: + * + * - Every table is part of the multi-tenant application, only explicitly + * excluded tables are non-federated. The behavior of the tables being in + * global or federated database is undefined. It depends on you selecting a + * federation before DDL statements or not. + * - Every Primary key of a federated table is extended by another column + * 'tenant_id' with a default value of the SQLAzure function + * `federation_filtering_value('tenant_id')`. + * - You always have to work with `filtering=On` when using federations with this + * multi-tenant approach. + * - Primary keys are either using globally unique ids (GUID, Table Generator) + * or you explicitly add the tenent_id in every UPDATE or DELETE statement + * (otherwise they will affect the same-id rows from other tenents as well). + * SQLAzure throws errors when you try to create IDENTIY columns on federated + * tables. + * + * @author Benjamin Eberlei + */ +class MultiTenantVisitor implements Visitor +{ + /** + * @var array + */ + private $excludedTables = array(); + + /** + * @var string + */ + private $tenantColumnName; + + /** + * @var string + */ + private $tenantColumnType = 'integer'; + + /** + * Name of the federation distribution, defaulting to the tenantColumnName + * if not specified. + * + * @var string + */ + private $distributionName; + + /** + * @param array $excludedTables + * @param string $tenantColumnName + * @param string|null $distributionName + */ + public function __construct(array $excludedTables = array(), $tenantColumnName = 'tenant_id', $distributionName = null) + { + $this->excludedTables = $excludedTables; + $this->tenantColumnName = $tenantColumnName; + $this->distributionName = $distributionName ?: $tenantColumnName; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + if (in_array($table->getName(), $this->excludedTables)) { + return; + } + + $table->addColumn($this->tenantColumnName, $this->tenantColumnType, array( + 'default' => "federation_filtering_value('". $this->distributionName ."')", + )); + + $clusteredIndex = $this->getClusteredIndex($table); + + $indexColumns = $clusteredIndex->getColumns(); + $indexColumns[] = $this->tenantColumnName; + + if ($clusteredIndex->isPrimary()) { + $table->dropPrimaryKey(); + $table->setPrimaryKey($indexColumns); + } else { + $table->dropIndex($clusteredIndex->getName()); + $table->addIndex($indexColumns, $clusteredIndex->getName()); + $table->getIndex($clusteredIndex->getName())->addFlag('clustered'); + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return \Doctrine\DBAL\Schema\Index + * + * @throws \RuntimeException + */ + private function getClusteredIndex($table) + { + foreach ($table->getIndexes() as $index) { + if ($index->isPrimary() && ! $index->hasFlag('nonclustered')) { + return $index; + } elseif ($index->hasFlag('clustered')) { + return $index; + } + } + throw new \RuntimeException("No clustered index found on table " . $table->getName()); + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php new file mode 100644 index 00000000000..f1d51b8c957 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * The MultiTenant Shard choser assumes that the distribution value directly + * maps to the shard id. + * + * @author Benjamin Eberlei + */ +class MultiTenantShardChoser implements ShardChoser +{ + /** + * {@inheritdoc} + */ + public function pickShard($distributionValue, PoolingShardConnection $conn) + { + return $distributionValue; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php new file mode 100644 index 00000000000..a7b85a66bbc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * Given a distribution value this shard-choser strategy will pick the shard to + * connect to for retrieving rows with the distribution value. + * + * @author Benjamin Eberlei + */ +interface ShardChoser +{ + /** + * Picks a shard for the given distribution value. + * + * @param string $distributionValue + * @param \Doctrine\DBAL\Sharding\PoolingShardConnection $conn + * + * @return integer + */ + function pickShard($distributionValue, PoolingShardConnection $conn); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php new file mode 100644 index 00000000000..b742793b815 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +/** + * Sharding Manager gives access to APIs to implementing sharding on top of + * Doctrine\DBAL\Connection instances. + * + * For simplicity and developer ease-of-use (and understanding) the sharding + * API only covers single shard queries, no fan-out support. It is primarily + * suited for multi-tenant applications. + * + * The assumption about sharding here + * is that a distribution value can be found that gives access to all the + * necessary data for all use-cases. Switching between shards should be done with + * caution, especially if lazy loading is implemented. Any query is always + * executed against the last shard that was selected. If a query is created for + * a shard Y but then a shard X is selected when its actually executed you + * will hit the wrong shard. + * + * @author Benjamin Eberlei + */ +interface ShardManager +{ + /** + * Selects global database with global data. + * + * This is the default database that is connected when no shard is + * selected. + * + * @return void + */ + function selectGlobal(); + + /** + * Selects the shard against which the queries after this statement will be issued. + * + * @param string $distributionValue + * + * @return void + * + * @throws \Doctrine\DBAL\Sharding\ShardingException If no value is passed as shard identifier. + */ + function selectShard($distributionValue); + + /** + * Gets the distribution value currently used for sharding. + * + * @return string + */ + function getCurrentDistributionValue(); + + /** + * Gets information about the amount of shards and other details. + * + * Format is implementation specific, each shard is one element and has an + * 'id' attribute at least. + * + * @return array + */ + function getShards(); + + /** + * Queries all shards in undefined order and return the results appended to + * each other. Restore the previous distribution value after execution. + * + * Using {@link \Doctrine\DBAL\Connection::fetchAll} to retrieve rows internally. + * + * @param string $sql + * @param array $params + * @param array $types + * + * @return array + */ + function queryAll($sql, array $params, array $types); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php new file mode 100644 index 00000000000..407bf847d4d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\DBALException; + +/** + * Sharding related Exceptions + * + * @since 2.3 + */ +class ShardingException extends DBALException +{ + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function notImplemented() + { + return new self("This functionality is not implemented with this sharding provider.", 1331557937); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDefaultFederationName() + { + return new self("SQLAzure requires a federation name to be set during sharding configuration.", 1332141280); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDefaultDistributionKey() + { + return new self("SQLAzure requires a distribution key to be set during sharding configuration.", 1332141329); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function activeTransaction() + { + return new self("Cannot switch shard during an active transaction.", 1332141766); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function noShardDistributionValue() + { + return new self("You have to specify a string or integer as shard distribution value.", 1332142103); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDistributionType() + { + return new self("You have to specify a sharding distribution type such as 'integer', 'string', 'guid'."); + } +} 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 00000000000..25ec652d2df --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php @@ -0,0 +1,312 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO; +use Doctrine\DBAL\Types\Type; +use 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 +{ + /** + * The SQL statement. + * + * @var string + */ + protected $sql; + + /** + * The bound parameters. + * + * @var array + */ + protected $params = array(); + + /** + * The parameter types. + * + * @var array + */ + protected $types = array(); + + /** + * The underlying driver statement. + * + * @var \Doctrine\DBAL\Driver\Statement + */ + protected $stmt; + + /** + * The underlying database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The connection this statement is bound to and executed on. + * + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * Creates a new Statement for the given SQL and Connection. + * + * @param string $sql The SQL of the statement. + * @param \Doctrine\DBAL\Connection $conn 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 string $name The name or position of the parameter. + * @param mixed $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; + $this->types[$name] = $type; + 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 $var The reference to the variable to bind. + * @param integer $type The PDO binding type. + * @param integer|null $length Must be specified when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindParam($name, &$var, $type = PDO::PARAM_STR, $length = null) + { + return $this->stmt->bindParam($name, $var, $type, $length); + } + + /** + * Executes the statement with the currently bound parameters. + * + * @param array|null $params + * + * @return boolean TRUE on success, FALSE on failure. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function execute($params = null) + { + if (is_array($params)) { + $this->params = $params; + } + + $logger = $this->conn->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($this->sql, $this->params, $this->types); + } + + try { + $stmt = $this->stmt->execute($params); + } catch (\Exception $ex) { + if ($logger) { + $logger->stopQuery(); + } + throw DBALException::driverExceptionDuringQuery( + $this->conn->getDriver(), + $ex, + $this->sql, + $this->conn->resolveParams($this->params, $this->types) + ); + } + + if ($logger) { + $logger->stopQuery(); + } + $this->params = array(); + $this->types = 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(); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 === null) { + return $this->stmt->setFetchMode($fetchMode); + } elseif ($arg3 === null) { + return $this->stmt->setFetchMode($fetchMode, $arg2); + } + + return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritdoc} + */ + public function getIterator() + { + return $this->stmt; + } + + /** + * Fetches the next row from a result set. + * + * @param integer|null $fetchMode + * + * @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($fetchMode = null) + { + return $this->stmt->fetch($fetchMode); + } + + /** + * Returns an array containing all of the result set rows. + * + * @param integer|null $fetchMode + * @param mixed $fetchArgument + * + * @return array An array containing all of the remaining rows in the result set. + */ + public function fetchAll($fetchMode = null, $fetchArgument = 0) + { + if ($fetchArgument !== 0) { + return $this->stmt->fetchAll($fetchMode, $fetchArgument); + } + + return $this->stmt->fetchAll($fetchMode); + } + + /** + * 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 00000000000..4d76ca92b7a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ImportCommand extends Command +{ + /** + * {@inheritdoc} + */ + 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) { + $filePath = realpath($fileName); + + // Phar compatibility. + if (false === $filePath) { + $filePath = $fileName; + } + + if ( ! file_exists($filePath)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not exist.", $filePath) + ); + } elseif ( ! is_readable($filePath)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not have read permissions.", $filePath) + ); + } + + $output->write(sprintf("Processing file '%s'... ", $filePath)); + $sql = file_get_contents($filePath); + + 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 00000000000..f7acd0a86ee --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -0,0 +1,172 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; + +class ReservedWordsCommand extends Command +{ + /** + * @var array + */ + private $keywordListClasses = array( + 'mysql' => 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords', + 'mysql57' => 'Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords', + 'sqlserver' => 'Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords', + 'sqlserver2005' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords', + 'sqlserver2008' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords', + 'sqlserver2012' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords', + 'sqlite' => 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords', + 'pgsql' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords', + 'pgsql91' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords', + 'pgsql92' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords', + 'oracle' => 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords', + 'db2' => 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords', + 'sqlanywhere' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords', + 'sqlanywhere11' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords', + 'sqlanywhere12' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords', + 'sqlanywhere16' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords', + ); + + /** + * If you want to add or replace a keywords list use this command. + * + * @param string $name + * @param string $class + * + * @return void + */ + public function setKeywordListClass($name, $class) + { + $this->keywordListClasses[$name] = $class; + } + + /** + * {@inheritdoc} + */ + 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(<<%command.full_name% + +If you want to check against specific dialects you can +pass them to the command: + + %command.full_name% mysql pgsql + +The following keyword lists are currently shipped with Doctrine: + + * mysql + * mysql57 + * pgsql + * pgsql92 + * sqlite + * oracle + * sqlserver + * sqlserver2005 + * sqlserver2008 + * sqlserver2012 + * sqlanywhere + * sqlanywhere11 + * sqlanywhere12 + * sqlanywhere16 + * db2 (Not checked by default) +EOT + ); + } + + /** + * {@inheritdoc} + */ + 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', + 'mysql57', + 'pgsql', + 'pgsql92', + 'sqlite', + 'oracle', + 'sqlserver', + 'sqlserver2005', + 'sqlserver2008', + 'sqlserver2012', + 'sqlanywhere', + 'sqlanywhere11', + 'sqlanywhere12', + 'sqlanywhere16', + ); + } + + $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); + } + + return 1; + } + } +} 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 00000000000..b2b2d97004e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunSqlCommand extends Command +{ + /** + * {@inheritdoc} + */ + 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 (stripos($sql, 'select') === 0) { + $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/ConsoleRunner.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php new file mode 100644 index 00000000000..3fb9c050cdd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Tools\Console\Command\ImportCommand; +use Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand; +use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Symfony\Component\Console\Application; +use Doctrine\DBAL\Version; + +/** + * Handles running the Console Tools inside Symfony Console context. + */ +class ConsoleRunner +{ + /** + * Create a Symfony Console HelperSet + * + * @param Connection $connection + * + * @return HelperSet + */ + static public function createHelperSet(Connection $connection) + { + return new HelperSet(array( + 'db' => new ConnectionHelper($connection) + )); + } + + /** + * Runs console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param \Symfony\Component\Console\Command\Command[] $commands + * + * @return void + */ + static public function run(HelperSet $helperSet, $commands = array()) + { + $cli = new Application('Doctrine Command Line Interface', Version::VERSION); + + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + + self::addCommands($cli); + + $cli->addCommands($commands); + $cli->run(); + } + + /** + * @param Application $cli + * + * @return void + */ + static public function addCommands(Application $cli) + { + $cli->addCommands(array( + new RunSqlCommand(), + new ImportCommand(), + new ReservedWordsCommand(), + )); + } + + /** + * Prints the instructions to create a configuration file + */ + static public function printCliConfigTemplate() + { + echo <<<'HELP' +You are missing a "cli-config.php" or "config/cli-config.php" file in your +project, which is required to get the Doctrine-DBAL Console working. You can use the +following sample as a template: + +. + */ + +namespace Doctrine\DBAL\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; +use Doctrine\DBAL\Connection; + +/** + * Doctrine CLI Connection Helper. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConnectionHelper extends Helper +{ + /** + * The Doctrine database Connection. + * + * @var \Doctrine\DBAL\Connection + */ + protected $_connection; + + /** + * Constructor. + * + * @param \Doctrine\DBAL\Connection $connection The Doctrine database Connection. + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * Retrieves the Doctrine database Connection. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..f8c62cb7a90 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP array to a clob SQL type. + * + * @since 2.0 + */ +class ArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + // @todo 3.0 - $value === null check to save real NULL in database + return serialize($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, 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; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} 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 00000000000..1a9a6c37fce --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php @@ -0,0 +1,63 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BIGINT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (string) $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php new file mode 100644 index 00000000000..a22a4405425 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps ab SQL BINARY/VARBINARY to a PHP resource stream. + * + * @author Steve Müller + * @since 2.5 + */ +class BinaryType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBinaryTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $fp = fopen('php://temp', 'rb+'); + fwrite($fp, $value); + fseek($fp, 0); + $value = $fp; + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BINARY); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BINARY; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_LOB; + } +} 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 00000000000..bd89301e21b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php @@ -0,0 +1,77 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBlobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $fp = fopen('php://temp', 'rb+'); + fwrite($fp, $value); + fseek($fp, 0); + $value = $fp; + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BLOB); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BLOB; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..9fea80f10a2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php @@ -0,0 +1,70 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBooleanTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $platform->convertBooleansToDatabaseValue($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $platform->convertFromBoolean($value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BOOLEAN; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_BOOL; + } +} 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 00000000000..4675101b305 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php @@ -0,0 +1,68 @@ +. + */ + +/** + * Conversion Exception is thrown when the database to PHP conversion fails. + * + * @link www.doctrine-project.org + * @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 \Doctrine\DBAL\Types\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 + * @param string $expectedFormat + * + * @return \Doctrine\DBAL\Types\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 + ); + } +} 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 00000000000..c5715a795c0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php @@ -0,0 +1,77 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIME; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); + + if ( ! $val) { + $val = date_create($value); + } + + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeFormatString()); + } + + return $val; + } +} 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 00000000000..2a9c6fa7b74 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php @@ -0,0 +1,90 @@ +. + */ + +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. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DateTimeTzType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIMETZ; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTzTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeTzFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeTzFormatString()); + } + + return $val; + } +} 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 00000000000..9d337558639 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php @@ -0,0 +1,72 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATE; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat('!'.$platform->getDateFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateFormatString()); + } + + return $val; + } +} 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 00000000000..df251d71751 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DECIMAL to a PHP string. + * + * @since 2.0 + */ +class DecimalType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DECIMAL; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : $value; + } +} 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 00000000000..87f9c32ae1a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class FloatType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::FLOAT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getFloatDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (double) $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php new file mode 100644 index 00000000000..761d58a8e85 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Represents a GUID/UUID datatype (both are actually synonyms) in the database. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class GuidType extends StringType +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getGuidTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::GUID; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return !$platform->hasNativeGuidType(); + } +} 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 00000000000..6fe6375ec81 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php @@ -0,0 +1,63 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::INTEGER; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php new file mode 100644 index 00000000000..79c3259411b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used to generate json arrays. + * + * @since 2.3 + * @author Johannes M. Schmitt + */ +class JsonArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getJsonTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + return json_encode($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value === '') { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return json_decode($value, true); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::JSON_ARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return ! $platform->hasNativeJsonType(); + } +} 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 00000000000..0b1f87a23c7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP object to a clob SQL type. + * + * @since 2.0 + */ +class ObjectType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return serialize($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, 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; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::OBJECT; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php new file mode 100644 index 00000000000..47c821be4e4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used for simple values. + * + * Only use this type if you are sure that your values cannot contain a ",". + * + * @since 2.3 + * @author Johannes M. Schmitt + */ +class SimpleArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!$value) { + return null; + } + + return implode(',', $value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return explode(',', $value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::SIMPLE_ARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} 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 00000000000..7ebae6ebe2a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php @@ -0,0 +1,62 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::SMALLINT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} 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 00000000000..39bd32dbc8e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php @@ -0,0 +1,54 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return $platform->getVarcharDefaultLength(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::STRING; + } +} 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 00000000000..4055ac88d22 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php @@ -0,0 +1,54 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (is_resource($value)) ? stream_get_contents($value) : $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TEXT; + } +} 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 00000000000..554d56dc062 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php @@ -0,0 +1,72 @@ +. + */ + +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 +{ + /** + * {@inheritdoc} + */ + 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 || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat('!' . $platform->getTimeFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getTimeFormatString()); + } + + return $val; + } +} 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 00000000000..9fa464f58b0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php @@ -0,0 +1,341 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use 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 SIMPLE_ARRAY = 'simple_array'; + const JSON_ARRAY = 'json_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 BINARY = 'binary'; + const BLOB = 'blob'; + const FLOAT = 'float'; + const GUID = 'guid'; + + /** + * Map of already instantiated type objects. One instance per type (flyweight). + * + * @var array + */ + private static $_typeObjects = array(); + + /** + * The map of supported doctrine mapping types. + * + * @var array + */ + private static $_typesMap = array( + self::TARRAY => 'Doctrine\DBAL\Types\ArrayType', + self::SIMPLE_ARRAY => 'Doctrine\DBAL\Types\SimpleArrayType', + self::JSON_ARRAY => 'Doctrine\DBAL\Types\JsonArrayType', + 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::BINARY => 'Doctrine\DBAL\Types\BinaryType', + self::BLOB => 'Doctrine\DBAL\Types\BlobType', + self::GUID => 'Doctrine\DBAL\Types\GuidType', + ); + + /** + * Prevents instantiation and forces 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 \Doctrine\DBAL\Platforms\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 \Doctrine\DBAL\Platforms\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. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return integer|null + * + * @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 \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform. + * + * @return string + */ + 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. + * + * @param string $name The name of the type (as returned by getName()). + * + * @return \Doctrine\DBAL\Types\Type + * + * @throws \Doctrine\DBAL\DBALException + */ + 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. + * + * @param string $name The name of the type. This should correspond to what getName() returns. + * @param string $className The class name of the custom type. + * + * @return void + * + * @throws \Doctrine\DBAL\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. + * + * @param string $name The 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. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws \Doctrine\DBAL\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; + } + + /** + * Gets the types array map which holds all registered types and the corresponding + * type class + * + * @return array + */ + public static function getTypesMap() + { + return self::$_typesMap; + } + + /** + * @return string + */ + 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 boolean + */ + public function canRequireSQLConversion() + { + return false; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a database value. + * + * @param string $sqlExpr + * @param \Doctrine\DBAL\Platforms\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 \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function convertToPHPValueSQL($sqlExpr, $platform) + { + return $sqlExpr; + } + + /** + * Gets an array of database types that map to this Doctrine type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMappedDatabaseTypes(AbstractPlatform $platform) + { + return array(); + } + + /** + * If this Doctrine Type maps to an already mapped database type, + * reverse schema engineering can't take them apart. You need to mark + * one of those types as commented, which will have Doctrine use an SQL + * comment to typehint the actual Doctrine Type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return boolean + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return false; + } +} 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 00000000000..5cf35f79583 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php @@ -0,0 +1,56 @@ +. + */ + +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. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class VarDateTimeType extends DateTimeType +{ + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = date_create($value); + if ( ! $val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } +} 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 00000000000..d8028774019 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Class to store and retrieve the version of Doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version. + */ + const VERSION = '2.5.4'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version The Doctrine version to compare to. + * + * @return integer -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/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php new file mode 100644 index 00000000000..53e2d3ab781 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Contract for a driver that is able to create platform instances by version. + * + * Doctrine uses different platform classes for different vendor versions to + * support the correct features and SQL syntax of each version. + * This interface should be implemented by drivers that are capable to do this + * distinction. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface VersionAwarePlatformDriver +{ + /** + * Factory method for creating the appropriate platform instance for the given version. + * + * @param string $version The platform/server version string to evaluate. This should be given in the notation + * the underlying database vendor uses. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + * + * @throws DBALException if the given version string could not be evaluated. + */ + public function createDatabasePlatformForVersion($version); +} diff --git a/vendor/doctrine/inflector/.gitignore b/vendor/doctrine/inflector/.gitignore new file mode 100644 index 00000000000..f2cb7f83eee --- /dev/null +++ b/vendor/doctrine/inflector/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +composer.phar +phpunit.xml diff --git a/vendor/doctrine/inflector/.travis.yml b/vendor/doctrine/inflector/.travis.yml new file mode 100644 index 00000000000..9ec68f7650c --- /dev/null +++ b/vendor/doctrine/inflector/.travis.yml @@ -0,0 +1,21 @@ +language: php + +sudo: false + +cache: + directory: + - $HOME/.composer/cache + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +install: + - composer install -n + +script: + - phpunit diff --git a/vendor/doctrine/inflector/LICENSE b/vendor/doctrine/inflector/LICENSE new file mode 100644 index 00000000000..8c38cc1bc22 --- /dev/null +++ b/vendor/doctrine/inflector/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 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/inflector/README.md b/vendor/doctrine/inflector/README.md new file mode 100644 index 00000000000..acb55a014bd --- /dev/null +++ b/vendor/doctrine/inflector/README.md @@ -0,0 +1,6 @@ +# Doctrine Inflector + +Doctrine Inflector is a small library that can perform string manipulations +with regard to upper-/lowercase and singular/plural forms of words. + +[![Build Status](https://travis-ci.org/doctrine/inflector.svg?branch=master)](https://travis-ci.org/doctrine/inflector) diff --git a/vendor/doctrine/inflector/composer.json b/vendor/doctrine/inflector/composer.json new file mode 100644 index 00000000000..7e5b2efbfdb --- /dev/null +++ b/vendor/doctrine/inflector/composer.json @@ -0,0 +1,29 @@ +{ + "name": "doctrine/inflector", + "type": "library", + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "keywords": ["string", "inflection", "singularize", "pluralize"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "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" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Inflector\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php b/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php new file mode 100644 index 00000000000..a53828aba95 --- /dev/null +++ b/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php @@ -0,0 +1,482 @@ +. + */ + +namespace Doctrine\Common\Inflector; + +/** + * 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. + * + * Pluralize & Singularize implementation are borrowed from CakePHP with some modifications. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Konsta Vesterinen + * @author Jonathan H. Wage + */ +class Inflector +{ + /** + * Plural inflector rules. + * + * @var array + */ + private static $plural = array( + 'rules' => array( + '/(s)tatus$/i' => '\1\2tatuses', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(f)oot$/i' => '\1eet', + '/(buffal|her|potat|tomat|volcan)o$/i' => '\1\2oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/i' => 'uses', + '/(alias)$/i' => '\1es', + '/(analys|ax|cris|test|thes)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's', + ), + 'uninflected' => array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people', 'cookie' + ), + 'irregular' => array( + 'atlas' => 'atlases', + 'axe' => 'axes', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'cafe' => 'cafes', + 'chateau' => 'chateaux', + 'child' => 'children', + 'cookie' => 'cookies', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'criterion' => 'criteria', + 'curriculum' => 'curricula', + 'demo' => 'demos', + 'domino' => 'dominoes', + 'echo' => 'echoes', + 'foot' => 'feet', + 'fungus' => 'fungi', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hippopotamus' => 'hippopotami', + 'hoof' => 'hoofs', + 'human' => 'humans', + 'iris' => 'irises', + 'leaf' => 'leaves', + 'loaf' => 'loaves', + 'man' => 'men', + 'medium' => 'media', + 'memorandum' => 'memoranda', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'motto' => 'mottoes', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'niche' => 'niches', + 'nucleus' => 'nuclei', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'person' => 'people', + 'plateau' => 'plateaux', + 'runner-up' => 'runners-up', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'son-in-law' => 'sons-in-law', + 'syllabus' => 'syllabi', + 'testis' => 'testes', + 'thief' => 'thieves', + 'tooth' => 'teeth', + 'tornado' => 'tornadoes', + 'trilby' => 'trilbys', + 'turf' => 'turfs', + 'volcano' => 'volcanoes', + ) + ); + + /** + * Singular inflector rules. + * + * @var array + */ + private static $singular = array( + 'rules' => array( + '/(s)tatuses$/i' => '\1\2tatus', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/i' => '\1', + '/(analys|ax|cris|test|thes)es$/i' => '\1is', + '/(shoe|slave)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/([^a])uses$/' => '\1us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(f)eet$/i' => '\1oot', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => '', + ), + 'uninflected' => array( + '.*[nrlm]ese', + '.*deer', + '.*fish', + '.*measles', + '.*ois', + '.*pox', + '.*sheep', + '.*ss', + ), + 'irregular' => array( + 'criteria' => 'criterion', + 'curves' => 'curve', + 'emphases' => 'emphasis', + 'foes' => 'foe', + 'hoaxes' => 'hoax', + 'media' => 'medium', + 'neuroses' => 'neurosis', + 'waves' => 'wave', + 'oases' => 'oasis', + ) + ); + + /** + * Words that should not be inflected. + * + * @var array + */ + private static $uninflected = array( + 'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', + 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', + 'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder', + 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', + 'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', '.*?media', + 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', + 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', + 'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', + 'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'staff', 'swine', + 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting', + 'wildebeest', 'Yengeese' + ); + + /** + * Method cache array. + * + * @var array + */ + private static $cache = array(); + + /** + * The initial state of Inflector so reset() works. + * + * @var array + */ + private static $initialState = array(); + + /** + * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'. + * + * @param string $word The word to tableize. + * + * @return string The tableized word. + */ + public static function tableize($word) + { + return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); + } + + /** + * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'. + * + * @param string $word The word to classify. + * + * @return string The classified word. + */ + public static function classify($word) + { + return str_replace(" ", "", ucwords(strtr($word, "_-", " "))); + } + + /** + * Camelizes a word. This uses the classify() method and turns the first character to lowercase. + * + * @param string $word The word to camelize. + * + * @return string The camelized word. + */ + public static function camelize($word) + { + return lcfirst(self::classify($word)); + } + + /** + * Uppercases words with configurable delimeters between words. + * + * Takes a string and capitalizes all of the words, like PHP's built-in + * ucwords function. This extends that behavior, however, by allowing the + * word delimeters to be configured, rather than only separating on + * whitespace. + * + * Here is an example: + * + * + * + * + * @param string $string The string to operate on. + * @param string $delimiters A list of word separators. + * + * @return string The string with all delimeter-separated words capitalized. + */ + public static function ucwords($string, $delimiters = " \n\t\r\0\x0B-") + { + return preg_replace_callback( + '/[^' . preg_quote($delimiters, '/') . ']+/', + function($matches) { + return ucfirst($matches[0]); + }, + $string + ); + } + + /** + * Clears Inflectors inflected value caches, and resets the inflection + * rules to the initial values. + * + * @return void + */ + public static function reset() + { + if (empty(self::$initialState)) { + self::$initialState = get_class_vars('Inflector'); + + return; + } + + foreach (self::$initialState as $key => $val) { + if ($key != 'initialState') { + self::${$key} = $val; + } + } + } + + /** + * Adds custom inflection $rules, of either 'plural' or 'singular' $type. + * + * ### Usage: + * + * {{{ + * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables')); + * Inflector::rules('plural', array( + * 'rules' => array('/^(inflect)ors$/i' => '\1ables'), + * 'uninflected' => array('dontinflectme'), + * 'irregular' => array('red' => 'redlings') + * )); + * }}} + * + * @param string $type The type of inflection, either 'plural' or 'singular' + * @param array $rules An array of rules to be added. + * @param boolean $reset If true, will unset default inflections for all + * new rules that are being defined in $rules. + * + * @return void + */ + public static function rules($type, $rules, $reset = false) + { + foreach ($rules as $rule => $pattern) { + if ( ! is_array($pattern)) { + continue; + } + + if ($reset) { + self::${$type}[$rule] = $pattern; + } else { + self::${$type}[$rule] = ($rule === 'uninflected') + ? array_merge($pattern, self::${$type}[$rule]) + : $pattern + self::${$type}[$rule]; + } + + unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]); + + if (isset(self::${$type}['merged'][$rule])) { + unset(self::${$type}['merged'][$rule]); + } + + if ($type === 'plural') { + self::$cache['pluralize'] = self::$cache['tableize'] = array(); + } elseif ($type === 'singular') { + self::$cache['singularize'] = array(); + } + } + + self::${$type}['rules'] = $rules + self::${$type}['rules']; + } + + /** + * Returns a word in plural form. + * + * @param string $word The word in singular form. + * + * @return string The word in plural form. + */ + public static function pluralize($word) + { + if (isset(self::$cache['pluralize'][$word])) { + return self::$cache['pluralize'][$word]; + } + + if (!isset(self::$plural['merged']['irregular'])) { + self::$plural['merged']['irregular'] = self::$plural['irregular']; + } + + if (!isset(self::$plural['merged']['uninflected'])) { + self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected); + } + + if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) { + self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')'; + self::$plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1); + + return self::$cache['pluralize'][$word]; + } + + if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$cache['pluralize'][$word] = $word; + + return $word; + } + + foreach (self::$plural['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word); + + return self::$cache['pluralize'][$word]; + } + } + } + + /** + * Returns a word in singular form. + * + * @param string $word The word in plural form. + * + * @return string The word in singular form. + */ + public static function singularize($word) + { + if (isset(self::$cache['singularize'][$word])) { + return self::$cache['singularize'][$word]; + } + + if (!isset(self::$singular['merged']['uninflected'])) { + self::$singular['merged']['uninflected'] = array_merge( + self::$singular['uninflected'], + self::$uninflected + ); + } + + if (!isset(self::$singular['merged']['irregular'])) { + self::$singular['merged']['irregular'] = array_merge( + self::$singular['irregular'], + array_flip(self::$plural['irregular']) + ); + } + + if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) { + self::$singular['cacheUninflected'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')'; + self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1); + + return self::$cache['singularize'][$word]; + } + + if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$cache['singularize'][$word] = $word; + + return $word; + } + + foreach (self::$singular['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word); + + return self::$cache['singularize'][$word]; + } + } + + self::$cache['singularize'][$word] = $word; + + return $word; + } +} diff --git a/vendor/doctrine/inflector/phpunit.xml.dist b/vendor/doctrine/inflector/phpunit.xml.dist new file mode 100644 index 00000000000..ef07faa53c8 --- /dev/null +++ b/vendor/doctrine/inflector/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php b/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php new file mode 100644 index 00000000000..4198d22c1da --- /dev/null +++ b/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php @@ -0,0 +1,309 @@ +assertEquals( + $singular, + Inflector::singularize($plural), + "'$plural' should be singularized to '$singular'" + ); + } + + /** + * testInflectingPlurals method + * + * @dataProvider dataSampleWords + * @return void + */ + public function testInflectingPlurals($singular, $plural) + { + $this->assertEquals( + $plural, + Inflector::pluralize($singular), + "'$singular' should be pluralized to '$plural'" + ); + } + + /** + * testCustomPluralRule method + * + * @return void + */ + public function testCustomPluralRule() + { + Inflector::reset(); + Inflector::rules('plural', array('/^(custom)$/i' => '\1izables')); + + $this->assertEquals(Inflector::pluralize('custom'), 'customizables'); + + Inflector::rules('plural', array('uninflected' => array('uninflectable'))); + + $this->assertEquals(Inflector::pluralize('uninflectable'), 'uninflectable'); + + Inflector::rules('plural', array( + 'rules' => array('/^(alert)$/i' => '\1ables'), + 'uninflected' => array('noflect', 'abtuse'), + 'irregular' => array('amaze' => 'amazable', 'phone' => 'phonezes') + )); + + $this->assertEquals(Inflector::pluralize('noflect'), 'noflect'); + $this->assertEquals(Inflector::pluralize('abtuse'), 'abtuse'); + $this->assertEquals(Inflector::pluralize('alert'), 'alertables'); + $this->assertEquals(Inflector::pluralize('amaze'), 'amazable'); + $this->assertEquals(Inflector::pluralize('phone'), 'phonezes'); + } + + /** + * testCustomSingularRule method + * + * @return void + */ + public function testCustomSingularRule() + { + Inflector::reset(); + Inflector::rules('singular', array('/(eple)r$/i' => '\1', '/(jente)r$/i' => '\1')); + + $this->assertEquals(Inflector::singularize('epler'), 'eple'); + $this->assertEquals(Inflector::singularize('jenter'), 'jente'); + + Inflector::rules('singular', array( + 'rules' => array('/^(bil)er$/i' => '\1', '/^(inflec|contribu)tors$/i' => '\1ta'), + 'uninflected' => array('singulars'), + 'irregular' => array('spins' => 'spinor') + )); + + $this->assertEquals(Inflector::singularize('inflectors'), 'inflecta'); + $this->assertEquals(Inflector::singularize('contributors'), 'contributa'); + $this->assertEquals(Inflector::singularize('spins'), 'spinor'); + $this->assertEquals(Inflector::singularize('singulars'), 'singulars'); + } + + /** + * test that setting new rules clears the inflector caches. + * + * @return void + */ + public function testRulesClearsCaches() + { + Inflector::reset(); + + $this->assertEquals(Inflector::singularize('Bananas'), 'Banana'); + $this->assertEquals(Inflector::pluralize('Banana'), 'Bananas'); + + Inflector::rules('singular', array( + 'rules' => array('/(.*)nas$/i' => '\1zzz') + )); + + $this->assertEquals('Banazzz', Inflector::singularize('Bananas'), 'Was inflected with old rules.'); + + Inflector::rules('plural', array( + 'rules' => array('/(.*)na$/i' => '\1zzz'), + 'irregular' => array('corpus' => 'corpora') + )); + + $this->assertEquals(Inflector::pluralize('Banana'), 'Banazzz', 'Was inflected with old rules.'); + $this->assertEquals(Inflector::pluralize('corpus'), 'corpora', 'Was inflected with old irregular form.'); + } + + /** + * Test resetting inflection rules. + * + * @return void + */ + public function testCustomRuleWithReset() + { + Inflector::reset(); + + $uninflected = array('atlas', 'lapis', 'onibus', 'pires', 'virus', '.*x'); + $pluralIrregular = array('as' => 'ases'); + + Inflector::rules('singular', array( + 'rules' => array('/^(.*)(a|e|o|u)is$/i' => '\1\2l'), + 'uninflected' => $uninflected, + ), true); + + Inflector::rules('plural', array( + 'rules' => array( + '/^(.*)(a|e|o|u)l$/i' => '\1\2is', + ), + 'uninflected' => $uninflected, + 'irregular' => $pluralIrregular + ), true); + + $this->assertEquals(Inflector::pluralize('Alcool'), 'Alcoois'); + $this->assertEquals(Inflector::pluralize('Atlas'), 'Atlas'); + $this->assertEquals(Inflector::singularize('Alcoois'), 'Alcool'); + $this->assertEquals(Inflector::singularize('Atlas'), 'Atlas'); + } + + /** + * Test basic ucwords functionality. + * + * @return void + */ + public function testUcwords() + { + $this->assertSame('Top-O-The-Morning To All_of_you!', Inflector::ucwords( 'top-o-the-morning to all_of_you!')); + } + + /** + * Test ucwords functionality with custom delimeters. + * + * @return void + */ + public function testUcwordsWithCustomDelimeters() + { + $this->assertSame('Top-O-The-Morning To All_Of_You!', Inflector::ucwords( 'top-o-the-morning to all_of_you!', '-_ ')); + } +} + diff --git a/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php b/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php new file mode 100644 index 00000000000..e8323d29409 --- /dev/null +++ b/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php @@ -0,0 +1,10 @@ + composer-installer.php + hhvm composer-installer.php + hhvm -v ResourceLimit.SocketDefaultTimeout=30 -v Http.SlowQueryThreshold=30000 composer.phar update --prefer-source +elif [ "$TRAVIS_PHP_VERSION" = '5.3.3' ] ; then + composer self-update + composer update --prefer-source --no-dev + composer dump-autoload +else + composer self-update + composer update --prefer-source +fi diff --git a/vendor/doctrine/instantiator/.travis.yml b/vendor/doctrine/instantiator/.travis.yml new file mode 100644 index 00000000000..7f1ec5f98d3 --- /dev/null +++ b/vendor/doctrine/instantiator/.travis.yml @@ -0,0 +1,22 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - ./.travis.install.sh + - if [ $TRAVIS_PHP_VERSION = '5.6' ]; then PHPUNIT_FLAGS="--coverage-clover coverage.clover"; else PHPUNIT_FLAGS=""; fi + +script: + - if [ $TRAVIS_PHP_VERSION = '5.3.3' ]; then phpunit; fi + - if [ $TRAVIS_PHP_VERSION != '5.3.3' ]; then ./vendor/bin/phpunit $PHPUNIT_FLAGS; fi + - if [ $TRAVIS_PHP_VERSION != '5.3.3' ]; then ./vendor/bin/phpcs --standard=PSR2 ./src/ ./tests/; fi + - if [[ $TRAVIS_PHP_VERSION != '5.3.3' && $TRAVIS_PHP_VERSION != '5.4.29' && $TRAVIS_PHP_VERSION != '5.5.13' ]]; then php -n ./vendor/bin/athletic -p ./tests/DoctrineTest/InstantiatorPerformance/ -f GroupedFormatter; fi + +after_script: + - if [ $TRAVIS_PHP_VERSION = '5.6' ]; then wget https://scrutinizer-ci.com/ocular.phar; php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/vendor/doctrine/instantiator/CONTRIBUTING.md b/vendor/doctrine/instantiator/CONTRIBUTING.md new file mode 100644 index 00000000000..75b84b2aa2e --- /dev/null +++ b/vendor/doctrine/instantiator/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing + + * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) + * The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php) + * Any contribution must provide tests for additional introduced conditions + * Any un-confirmed issue needs a failing test case before being accepted + * Pull requests must be sent from a new hotfix/feature branch, not from `master`. + +## Installation + +To install the project and run the tests, you need to clone it first: + +```sh +$ git clone git://github.com/doctrine/instantiator.git +``` + +You will then need to run a composer installation: + +```sh +$ cd Instantiator +$ curl -s https://getcomposer.org/installer | php +$ php composer.phar update +``` + +## Testing + +The PHPUnit version to be used is the one installed as a dev- dependency via composer: + +```sh +$ ./vendor/bin/phpunit +``` + +Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement +won't be merged. + diff --git a/vendor/doctrine/instantiator/LICENSE b/vendor/doctrine/instantiator/LICENSE new file mode 100644 index 00000000000..4d983d1ac70 --- /dev/null +++ b/vendor/doctrine/instantiator/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 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/instantiator/README.md b/vendor/doctrine/instantiator/README.md new file mode 100644 index 00000000000..393ec7caafc --- /dev/null +++ b/vendor/doctrine/instantiator/README.md @@ -0,0 +1,40 @@ +# Instantiator + +This library provides a way of avoiding usage of constructors when instantiating PHP classes. + +[![Build Status](https://travis-ci.org/doctrine/instantiator.svg?branch=master)](https://travis-ci.org/doctrine/instantiator) +[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) +[![Dependency Status](https://www.versioneye.com/package/php--doctrine--instantiator/badge.svg)](https://www.versioneye.com/package/php--doctrine--instantiator) +[![HHVM Status](http://hhvm.h4cc.de/badge/doctrine/instantiator.png)](http://hhvm.h4cc.de/package/doctrine/instantiator) + +[![Latest Stable Version](https://poser.pugx.org/doctrine/instantiator/v/stable.png)](https://packagist.org/packages/doctrine/instantiator) +[![Latest Unstable Version](https://poser.pugx.org/doctrine/instantiator/v/unstable.png)](https://packagist.org/packages/doctrine/instantiator) + +## Installation + +The suggested installation method is via [composer](https://getcomposer.org/): + +```sh +php composer.phar require "doctrine/instantiator:~1.0.3" +``` + +## Usage + +The instantiator is able to create new instances of any class without using the constructor or any API of the class +itself: + +```php +$instantiator = new \Doctrine\Instantiator\Instantiator(); + +$instance = $instantiator->instantiate('My\\ClassName\\Here'); +``` + +## Contributing + +Please read the [CONTRIBUTING.md](CONTRIBUTING.md) contents if you wish to help out! + +## Credits + +This library was migrated from [ocramius/instantiator](https://github.com/Ocramius/Instantiator), which +has been donated to the doctrine organization, and which is now deprecated in favour of this package. diff --git a/vendor/doctrine/instantiator/composer.json b/vendor/doctrine/instantiator/composer.json new file mode 100644 index 00000000000..4823890b4c7 --- /dev/null +++ b/vendor/doctrine/instantiator/composer.json @@ -0,0 +1,45 @@ +{ + "name": "doctrine/instantiator", + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "type": "library", + "license": "MIT", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "instantiate", + "constructor" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "ext-phar": "*", + "ext-pdo": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0", + "athletic/athletic": "~0.1.8" + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "autoload-dev": { + "psr-0": { + "DoctrineTest\\InstantiatorPerformance\\": "tests", + "DoctrineTest\\InstantiatorTest\\": "tests", + "DoctrineTest\\InstantiatorTestAsset\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/instantiator/phpmd.xml.dist b/vendor/doctrine/instantiator/phpmd.xml.dist new file mode 100644 index 00000000000..82541056275 --- /dev/null +++ b/vendor/doctrine/instantiator/phpmd.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/instantiator/phpunit.xml.dist b/vendor/doctrine/instantiator/phpunit.xml.dist new file mode 100644 index 00000000000..0a8d5709b7e --- /dev/null +++ b/vendor/doctrine/instantiator/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + ./tests/DoctrineTest/InstantiatorTest + + + + ./src + + + diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php new file mode 100644 index 00000000000..3065375a8ca --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +/** + * Base exception marker interface for the instantiator component + * + * @author Marco Pivetta + */ +interface ExceptionInterface +{ +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php new file mode 100644 index 00000000000..ea8d28c5c57 --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +use InvalidArgumentException as BaseInvalidArgumentException; +use ReflectionClass; + +/** + * Exception for invalid arguments provided to the instantiator + * + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface +{ + /** + * @param string $className + * + * @return self + */ + public static function fromNonExistingClass($className) + { + if (interface_exists($className)) { + return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className)); + } + + if (PHP_VERSION_ID >= 50400 && trait_exists($className)) { + return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); + } + + return new self(sprintf('The provided class "%s" does not exist', $className)); + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return self + */ + public static function fromAbstractClass(ReflectionClass $reflectionClass) + { + return new self(sprintf( + 'The provided class "%s" is abstract, and can not be instantiated', + $reflectionClass->getName() + )); + } +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php new file mode 100644 index 00000000000..1681e56e887 --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +use Exception; +use ReflectionClass; +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Exception for given parameters causing invalid/unexpected state on instantiation + * + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface +{ + /** + * @param ReflectionClass $reflectionClass + * @param Exception $exception + * + * @return self + */ + public static function fromSerializationTriggeredException(ReflectionClass $reflectionClass, Exception $exception) + { + return new self( + sprintf( + 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', + $reflectionClass->getName() + ), + 0, + $exception + ); + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $errorString + * @param int $errorCode + * @param string $errorFile + * @param int $errorLine + * + * @return UnexpectedValueException + */ + public static function fromUncleanUnSerialization( + ReflectionClass $reflectionClass, + $errorString, + $errorCode, + $errorFile, + $errorLine + ) { + return new self( + sprintf( + 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' + . 'in file "%s" at line "%d"', + $reflectionClass->getName(), + $errorFile, + $errorLine + ), + 0, + new Exception($errorString, $errorCode) + ); + } +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php new file mode 100644 index 00000000000..6d5b3b65675 --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php @@ -0,0 +1,273 @@ +. + */ + +namespace Doctrine\Instantiator; + +use Closure; +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use ReflectionClass; + +/** + * {@inheritDoc} + * + * @author Marco Pivetta + */ +final class Instantiator implements InstantiatorInterface +{ + /** + * Markers used internally by PHP to define whether {@see \unserialize} should invoke + * the method {@see \Serializable::unserialize()} when dealing with classes implementing + * the {@see \Serializable} interface. + */ + const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; + const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; + + /** + * @var \Closure[] of {@see \Closure} instances used to instantiate specific classes + */ + private static $cachedInstantiators = array(); + + /** + * @var object[] of objects that can directly be cloned + */ + private static $cachedCloneables = array(); + + /** + * {@inheritDoc} + */ + public function instantiate($className) + { + if (isset(self::$cachedCloneables[$className])) { + return clone self::$cachedCloneables[$className]; + } + + if (isset(self::$cachedInstantiators[$className])) { + $factory = self::$cachedInstantiators[$className]; + + return $factory(); + } + + return $this->buildAndCacheFromFactory($className); + } + + /** + * Builds the requested object and caches it in static properties for performance + * + * @param string $className + * + * @return object + */ + private function buildAndCacheFromFactory($className) + { + $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); + $instance = $factory(); + + if ($this->isSafeToClone(new ReflectionClass($instance))) { + self::$cachedCloneables[$className] = clone $instance; + } + + return $instance; + } + + /** + * Builds a {@see \Closure} capable of instantiating the given $className without + * invoking its constructor. + * + * @param string $className + * + * @return Closure + */ + private function buildFactory($className) + { + $reflectionClass = $this->getReflectionClass($className); + + if ($this->isInstantiableViaReflection($reflectionClass)) { + return function () use ($reflectionClass) { + return $reflectionClass->newInstanceWithoutConstructor(); + }; + } + + $serializedString = sprintf( + '%s:%d:"%s":0:{}', + $this->getSerializationFormat($reflectionClass), + strlen($className), + $className + ); + + $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); + + return function () use ($serializedString) { + return unserialize($serializedString); + }; + } + + /** + * @param string $className + * + * @return ReflectionClass + * + * @throws InvalidArgumentException + */ + private function getReflectionClass($className) + { + if (! class_exists($className)) { + throw InvalidArgumentException::fromNonExistingClass($className); + } + + $reflection = new ReflectionClass($className); + + if ($reflection->isAbstract()) { + throw InvalidArgumentException::fromAbstractClass($reflection); + } + + return $reflection; + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) + { + set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) { + $error = UnexpectedValueException::fromUncleanUnSerialization( + $reflectionClass, + $message, + $code, + $file, + $line + ); + }); + + $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); + + restore_error_handler(); + + if ($error) { + throw $error; + } + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) + { + try { + unserialize($serializedString); + } catch (Exception $exception) { + restore_error_handler(); + + throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); + } + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function isInstantiableViaReflection(ReflectionClass $reflectionClass) + { + if (\PHP_VERSION_ID >= 50600) { + return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); + } + + return \PHP_VERSION_ID >= 50400 && ! $this->hasInternalAncestors($reflectionClass); + } + + /** + * Verifies whether the given class is to be considered internal + * + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function hasInternalAncestors(ReflectionClass $reflectionClass) + { + do { + if ($reflectionClass->isInternal()) { + return true; + } + } while ($reflectionClass = $reflectionClass->getParentClass()); + + return false; + } + + /** + * Verifies if the given PHP version implements the `Serializable` interface serialization + * with an incompatible serialization format. If that's the case, use serialization marker + * "C" instead of "O". + * + * @link http://news.php.net/php.internals/74654 + * + * @param ReflectionClass $reflectionClass + * + * @return string the serialization format marker, either self::SERIALIZATION_FORMAT_USE_UNSERIALIZER + * or self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER + */ + private function getSerializationFormat(ReflectionClass $reflectionClass) + { + if ($this->isPhpVersionWithBrokenSerializationFormat() + && $reflectionClass->implementsInterface('Serializable') + ) { + return self::SERIALIZATION_FORMAT_USE_UNSERIALIZER; + } + + return self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER; + } + + /** + * Checks whether the current PHP runtime uses an incompatible serialization format + * + * @return bool + */ + private function isPhpVersionWithBrokenSerializationFormat() + { + return PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513; + } + + /** + * Checks if a class is cloneable + * + * @param ReflectionClass $reflection + * + * @return bool + */ + private function isSafeToClone(ReflectionClass $reflection) + { + if (method_exists($reflection, 'isCloneable') && ! $reflection->isCloneable()) { + return false; + } + + // not cloneable if it implements `__clone`, as we want to avoid calling it + return ! $reflection->hasMethod('__clone'); + } +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php new file mode 100644 index 00000000000..b665bea8547 --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Instantiator; + +/** + * Instantiator provides utility methods to build objects without invoking their constructors + * + * @author Marco Pivetta + */ +interface InstantiatorInterface +{ + /** + * @param string $className + * + * @return object + * + * @throws \Doctrine\Instantiator\Exception\ExceptionInterface + */ + public function instantiate($className); +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php new file mode 100644 index 00000000000..3e8fc6ff43a --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php @@ -0,0 +1,96 @@ +. + */ + +namespace DoctrineTest\InstantiatorPerformance; + +use Athletic\AthleticEvent; +use Doctrine\Instantiator\Instantiator; + +/** + * Performance tests for {@see \Doctrine\Instantiator\Instantiator} + * + * @author Marco Pivetta + */ +class InstantiatorPerformanceEvent extends AthleticEvent +{ + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->instantiator = new Instantiator(); + + $this->instantiator->instantiate(__CLASS__); + $this->instantiator->instantiate('ArrayObject'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'); + } + + /** + * @iterations 20000 + * @baseline + * @group instantiation + */ + public function testInstantiateSelf() + { + $this->instantiator->instantiate(__CLASS__); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateInternalClass() + { + $this->instantiator->instantiate('ArrayObject'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateSimpleSerializableAssetClass() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateSerializableArrayObjectAsset() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateUnCloneableAsset() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php new file mode 100644 index 00000000000..39d9b94dfa8 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php @@ -0,0 +1,83 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest\Exception; + +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Exception\InvalidArgumentException} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Exception\InvalidArgumentException + */ +class InvalidArgumentExceptionTest extends PHPUnit_Framework_TestCase +{ + public function testFromNonExistingTypeWithNonExistingClass() + { + $className = __CLASS__ . uniqid(); + $exception = InvalidArgumentException::fromNonExistingClass($className); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\InvalidArgumentException', $exception); + $this->assertSame('The provided class "' . $className . '" does not exist', $exception->getMessage()); + } + + public function testFromNonExistingTypeWithTrait() + { + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('Need at least PHP 5.4.0, as this test requires traits support to run'); + } + + $exception = InvalidArgumentException::fromNonExistingClass( + 'DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset' + ); + + $this->assertSame( + 'The provided type "DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset" is a trait, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } + + public function testFromNonExistingTypeWithInterface() + { + $exception = InvalidArgumentException::fromNonExistingClass('Doctrine\\Instantiator\\InstantiatorInterface'); + + $this->assertSame( + 'The provided type "Doctrine\\Instantiator\\InstantiatorInterface" is an interface, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } + + public function testFromAbstractClass() + { + $reflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'); + $exception = InvalidArgumentException::fromAbstractClass($reflection); + + $this->assertSame( + 'The provided class "DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset" is abstract, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php new file mode 100644 index 00000000000..84154e7397e --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php @@ -0,0 +1,69 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest\Exception; + +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Exception\UnexpectedValueException} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Exception\UnexpectedValueException + */ +class UnexpectedValueExceptionTest extends PHPUnit_Framework_TestCase +{ + public function testFromSerializationTriggeredException() + { + $reflectionClass = new ReflectionClass($this); + $previous = new Exception(); + $exception = UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $previous); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\UnexpectedValueException', $exception); + $this->assertSame($previous, $exception->getPrevious()); + $this->assertSame( + 'An exception was raised while trying to instantiate an instance of "' + . __CLASS__ . '" via un-serialization', + $exception->getMessage() + ); + } + + public function testFromUncleanUnSerialization() + { + $reflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'); + $exception = UnexpectedValueException::fromUncleanUnSerialization($reflection, 'foo', 123, 'bar', 456); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\UnexpectedValueException', $exception); + $this->assertSame( + 'Could not produce an instance of "DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset" ' + . 'via un-serialization, since an error was triggered in file "bar" at line "456"', + $exception->getMessage() + ); + + $previous = $exception->getPrevious(); + + $this->assertInstanceOf('Exception', $previous); + $this->assertSame('foo', $previous->getMessage()); + $this->assertSame(123, $previous->getCode()); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php new file mode 100644 index 00000000000..0a2cb9313d2 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php @@ -0,0 +1,219 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest; + +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Doctrine\Instantiator\Instantiator; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Instantiator} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Instantiator + */ +class InstantiatorTest extends PHPUnit_Framework_TestCase +{ + /** + * @var Instantiator + */ + private $instantiator; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->instantiator = new Instantiator(); + } + + /** + * @param string $className + * + * @dataProvider getInstantiableClasses + */ + public function testCanInstantiate($className) + { + $this->assertInstanceOf($className, $this->instantiator->instantiate($className)); + } + + /** + * @param string $className + * + * @dataProvider getInstantiableClasses + */ + public function testInstantiatesSeparateInstances($className) + { + $instance1 = $this->instantiator->instantiate($className); + $instance2 = $this->instantiator->instantiate($className); + + $this->assertEquals($instance1, $instance2); + $this->assertNotSame($instance1, $instance2); + } + + public function testExceptionOnUnSerializationException() + { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped( + 'As of facebook/hhvm#3432, HHVM has no PDORow, and therefore ' + . ' no internal final classes that cannot be instantiated' + ); + } + + $className = 'DoctrineTest\\InstantiatorTestAsset\\UnserializeExceptionArrayObjectAsset'; + + if (\PHP_VERSION_ID >= 50600) { + $className = 'PDORow'; + } + + if (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513) { + $className = 'DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'; + } + + $this->setExpectedException('Doctrine\\Instantiator\\Exception\\UnexpectedValueException'); + + $this->instantiator->instantiate($className); + } + + public function testNoticeOnUnSerializationException() + { + if (\PHP_VERSION_ID >= 50600) { + $this->markTestSkipped( + 'PHP 5.6 supports `ReflectionClass#newInstanceWithoutConstructor()` for some internal classes' + ); + } + + try { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + + $this->fail('No exception was raised'); + } catch (UnexpectedValueException $exception) { + $wakeUpNoticesReflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + $previous = $exception->getPrevious(); + + $this->assertInstanceOf('Exception', $previous); + + // in PHP 5.4.29 and PHP 5.5.13, this case is not a notice, but an exception being thrown + if (! (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513)) { + $this->assertSame( + 'Could not produce an instance of "DoctrineTest\\InstantiatorTestAsset\WakeUpNoticesAsset" ' + . 'via un-serialization, since an error was triggered in file "' + . $wakeUpNoticesReflection->getFileName() . '" at line "36"', + $exception->getMessage() + ); + + $this->assertSame('Something went bananas while un-serializing this instance', $previous->getMessage()); + $this->assertSame(\E_USER_NOTICE, $previous->getCode()); + } + } + } + + /** + * @param string $invalidClassName + * + * @dataProvider getInvalidClassNames + */ + public function testInstantiationFromNonExistingClass($invalidClassName) + { + $this->setExpectedException('Doctrine\\Instantiator\\Exception\\InvalidArgumentException'); + + $this->instantiator->instantiate($invalidClassName); + } + + public function testInstancesAreNotCloned() + { + $className = 'TemporaryClass' . uniqid(); + + eval('namespace ' . __NAMESPACE__ . '; class ' . $className . '{}'); + + $instance = $this->instantiator->instantiate(__NAMESPACE__ . '\\' . $className); + + $instance->foo = 'bar'; + + $instance2 = $this->instantiator->instantiate(__NAMESPACE__ . '\\' . $className); + + $this->assertObjectNotHasAttribute('foo', $instance2); + } + + /** + * Provides a list of instantiable classes (existing) + * + * @return string[][] + */ + public function getInstantiableClasses() + { + $classes = array( + array('stdClass'), + array(__CLASS__), + array('Doctrine\\Instantiator\\Instantiator'), + array('Exception'), + array('PharException'), + array('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\ExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\FinalExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\PharExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\XMLReaderAsset'), + ); + + if (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513) { + return $classes; + } + + $classes = array_merge( + $classes, + array( + array('PharException'), + array('ArrayObject'), + array('DoctrineTest\\InstantiatorTestAsset\\ArrayObjectAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'), + ) + ); + + if (\PHP_VERSION_ID >= 50600) { + $classes[] = array('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + $classes[] = array('DoctrineTest\\InstantiatorTestAsset\\UnserializeExceptionArrayObjectAsset'); + } + + return $classes; + } + + /** + * Provides a list of instantiable classes (existing) + * + * @return string[][] + */ + public function getInvalidClassNames() + { + $classNames = array( + array(__CLASS__ . uniqid()), + array('Doctrine\\Instantiator\\InstantiatorInterface'), + array('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'), + ); + + if (\PHP_VERSION_ID >= 50400) { + $classNames[] = array('DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset'); + } + + return $classNames; + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php new file mode 100644 index 00000000000..fbe28ddd0e7 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php @@ -0,0 +1,29 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +abstract class AbstractClassAsset +{ +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php new file mode 100644 index 00000000000..56146d7092a --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; + +/** + * Test asset that extends an internal PHP class + * + * @author Marco Pivetta + */ +class ArrayObjectAsset extends ArrayObject +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php new file mode 100644 index 00000000000..43bbe46b715 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Exception; + +/** + * Test asset that extends an internal PHP base exception + * + * @author Marco Pivetta + */ +class ExceptionAsset extends Exception +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php new file mode 100644 index 00000000000..7d268f5b3b7 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Exception; + +/** + * Test asset that extends an internal PHP base exception + * + * @author Marco Pivetta + */ +final class FinalExceptionAsset extends Exception +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php new file mode 100644 index 00000000000..553fd561262 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Phar; + +/** + * Test asset that extends an internal PHP class + * + * @author Marco Pivetta + */ +class PharAsset extends Phar +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php new file mode 100644 index 00000000000..42bf73e7eeb --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php @@ -0,0 +1,44 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use PharException; + +/** + * Test asset that extends an internal PHP class + * This class should be serializable without problems + * and without getting the "Erroneous data format for unserializing" + * error + * + * @author Marco Pivetta + */ +class PharExceptionAsset extends PharException +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php new file mode 100644 index 00000000000..ba19aaf63f9 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php @@ -0,0 +1,62 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; +use Serializable; + +/** + * Serializable test asset that also extends an internal class + * + * @author Marco Pivetta + */ +class SerializableArrayObjectAsset extends ArrayObject implements Serializable +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * {@inheritDoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritDoc} + * + * Should not be called + * + * @throws BadMethodCallException + */ + public function unserialize($serialized) + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php new file mode 100644 index 00000000000..39f84a6c0d7 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php @@ -0,0 +1,61 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Serializable; + +/** + * Base serializable test asset + * + * @author Marco Pivetta + */ +class SimpleSerializableAsset implements Serializable +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * {@inheritDoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritDoc} + * + * Should not be called + * + * @throws BadMethodCallException + */ + public function unserialize($serialized) + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php new file mode 100644 index 00000000000..04e78069d21 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php @@ -0,0 +1,29 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +/** + * A simple trait with no attached logic + * + * @author Marco Pivetta + */ +trait SimpleTraitAsset +{ +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php new file mode 100644 index 00000000000..7d03bdab0b6 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php @@ -0,0 +1,50 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; + +/** + * Base un-cloneable asset + * + * @author Marco Pivetta + */ +class UnCloneableAsset +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * Magic `__clone` - should not be invoked + * + * @throws BadMethodCallException + */ + public function __clone() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php new file mode 100644 index 00000000000..b348a40535b --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php @@ -0,0 +1,39 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +class UnserializeExceptionArrayObjectAsset extends ArrayObject +{ + /** + * {@inheritDoc} + */ + public function __wakeup() + { + throw new BadMethodCallException(); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php new file mode 100644 index 00000000000..18dc6711dc9 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php @@ -0,0 +1,38 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +class WakeUpNoticesAsset extends ArrayObject +{ + /** + * Wakeup method called after un-serialization + */ + public function __wakeup() + { + trigger_error('Something went bananas while un-serializing this instance'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php new file mode 100644 index 00000000000..39ee6992aee --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use XMLReader; + +/** + * Test asset that extends an internal PHP class + * + * @author Dave Marshall + */ +class XMLReaderAsset extends XMLReader +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/lexer/LICENSE b/vendor/doctrine/lexer/LICENSE new file mode 100644 index 00000000000..5e781fce4bb --- /dev/null +++ b/vendor/doctrine/lexer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 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/lexer/README.md b/vendor/doctrine/lexer/README.md new file mode 100644 index 00000000000..66f443089eb --- /dev/null +++ b/vendor/doctrine/lexer/README.md @@ -0,0 +1,5 @@ +# Doctrine Lexer + +Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers. + +This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL). diff --git a/vendor/doctrine/lexer/composer.json b/vendor/doctrine/lexer/composer.json new file mode 100644 index 00000000000..8cd694c6525 --- /dev/null +++ b/vendor/doctrine/lexer/composer.json @@ -0,0 +1,24 @@ +{ + "name": "doctrine/lexer", + "type": "library", + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "keywords": ["lexer", "parser"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php b/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php new file mode 100644 index 00000000000..399a55230b0 --- /dev/null +++ b/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php @@ -0,0 +1,327 @@ +. + */ + +namespace Doctrine\Common\Lexer; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractLexer +{ + /** + * Lexer original input string. + * + * @var string + */ + private $input; + + /** + * Array of scanned tokens. + * + * Each 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 + * + * @var array + */ + private $tokens = array(); + + /** + * Current lexer position in input string. + * + * @var integer + */ + private $position = 0; + + /** + * Current peek of current lexer position. + * + * @var integer + */ + private $peek = 0; + + /** + * The next token in the input. + * + * @var array + */ + public $lookahead; + + /** + * The last matched/seen token. + * + * @var array + */ + 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. + * + * @return void + */ + public function setInput($input) + { + $this->input = $input; + $this->tokens = array(); + + $this->reset(); + $this->scan($input); + } + + /** + * Resets the lexer. + * + * @return void + */ + public function reset() + { + $this->lookahead = null; + $this->token = null; + $this->peek = 0; + $this->position = 0; + } + + /** + * Resets the peek pointer to 0. + * + * @return void + */ + 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. + * + * @return void + */ + public function resetPosition($position = 0) + { + $this->position = $position; + } + + /** + * Retrieve the original lexer's input until a given position. + * + * @param integer $position + * + * @return string + */ + public function getInputUntilPosition($position) + { + return substr($this->input, 0, $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. + * + * @return boolean + */ + 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 string $type The token type to skip until. + * + * @return void + */ + 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. + * + * @return void + */ + protected function scan($input) + { + static $regex; + + if ( ! isset($regex)) { + $regex = sprintf( + '/(%s)|%s/%s', + implode(')|(', $this->getCatchablePatterns()), + implode('|', $this->getNonCatchablePatterns()), + $this->getModifiers() + ); + } + + $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; + } + + /** + * Regex modifiers + * + * @return string + */ + protected function getModifiers() + { + return 'i'; + } + + /** + * 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); +} diff --git a/vendor/doctrine/orm/.coveralls.yml b/vendor/doctrine/orm/.coveralls.yml new file mode 100644 index 00000000000..b086fff7a45 --- /dev/null +++ b/vendor/doctrine/orm/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: travis-ci +src_dir: lib +coverage_clover: build/logs/clover*.xml diff --git a/vendor/doctrine/orm/LICENSE b/vendor/doctrine/orm/LICENSE new file mode 100644 index 00000000000..4a91f0bf280 --- /dev/null +++ b/vendor/doctrine/orm/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 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/orm/README.markdown b/vendor/doctrine/orm/README.markdown new file mode 100644 index 00000000000..6cbccaf7996 --- /dev/null +++ b/vendor/doctrine/orm/README.markdown @@ -0,0 +1,30 @@ +| [Master][Master] | [2.4][2.4] | [2.3][2.3] | [2.2][2.2] | [2.1][2.1] | +|:----------------:|:----------:|:----------:|:----------:|:----------:| +| [![Build status][Master image]][Master] | [![Build status][2.4 image]][2.4] | [![Build status][2.3 image]][2.3] | [![Build status][2.2 image]][2.2] | [![Build status][2.1 image]][2.1] | +| [![Coverage Status][Master coverage image]][Master coverage] | + +Doctrine 2 is an object-relational mapper (ORM) for PHP 5.4+ 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 Hibernate's 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://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC) +* [Downloads](http://github.com/doctrine/doctrine2/downloads) + + [Master image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=master + [Master]: https://travis-ci.org/doctrine/doctrine2 + [Master coverage image]: https://coveralls.io/repos/doctrine/doctrine2/badge.png?branch=master + [Master coverage]: https://coveralls.io/r/doctrine/doctrine2?branch=master + [2.4 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.4 + [2.4]: https://github.com/doctrine/doctrine2/tree/2.4 + [2.3 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.3 + [2.3]: https://github.com/doctrine/doctrine2/tree/2.3 + [2.2 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.2 + [2.2]: https://github.com/doctrine/doctrine2/tree/2.2 + [2.1 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.1.x + [2.1]: https://github.com/doctrine/doctrine2/tree/2.1.x diff --git a/vendor/doctrine/orm/SECURITY.md b/vendor/doctrine/orm/SECURITY.md new file mode 100644 index 00000000000..313900211e6 --- /dev/null +++ b/vendor/doctrine/orm/SECURITY.md @@ -0,0 +1,18 @@ +Security +======== + +The Doctrine library is operating very close to your database and as such needs +to handle and make assumptions about SQL injection vulnerabilities. + +It is vital that you understand how Doctrine approaches security, because +we cannot protect you from SQL injection. + +Please read the documentation chapter on Security in Doctrine DBAL and ORM to +understand the assumptions we make. + +- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst) +- [ORM Security Page](https://github.com/doctrine/doctrine2/blob/master/docs/en/reference/security.rst) + +If you find a Security bug in Doctrine, please report it on Jira and change the +Security Level to "Security Issues". It will be visible to Doctrine Core +developers and you only. diff --git a/vendor/doctrine/orm/UPGRADE.md b/vendor/doctrine/orm/UPGRADE.md new file mode 100644 index 00000000000..ed9a538b3ec --- /dev/null +++ b/vendor/doctrine/orm/UPGRADE.md @@ -0,0 +1,675 @@ +# Upgrade to 2.5 + +## Minor BC BREAK: discriminator map must now include all non-transient classes + +It is now required that you declare the root of an inheritance in the +discriminator map. + +When declaring an inheritance map, it was previously possible to skip the root +of the inheritance in the discriminator map. This was actually a validation +mistake by Doctrine2 and led to problems when trying to persist instances of +that class. + +If you don't plan to persist instances some classes in your inheritance, then +either: + + - make those classes `abstract` + - map those classes as `MappedSuperclass` + +## Minor BC BREAK: ``EntityManagerInterface`` instead of ``EntityManager`` in type-hints + +As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require +an ``EntityManagerInterface`` instead. +If you are extending any of the following classes, then you need to check following +signatures: + +- ``Doctrine\ORM\Tools\DebugUnitOfWorkListener#dumpIdentityMap(EntityManagerInterface $em)`` +- ``Doctrine\ORM\Mapping\ClassMetadataFactory#setEntityManager(EntityManagerInterface $em)`` + +## Minor BC BREAK: Custom Hydrators API change + +As of 2.5, `AbstractHydrator` does not enforce the usage of cache as part of +API, and now provides you a clean API for column information through the method +`hydrateColumnInfo($column)`. +Cache variable being passed around by reference is no longer needed since +Hydrators are per query instantiated since Doctrine 2.4. + +## Minor BC BREAK: Entity based ``EntityManager#clear()`` calls follow cascade detach + +Whenever ``EntityManager#clear()`` method gets called with a given entity class +name, until 2.4, it was only detaching the specific requested entity. +As of 2.5, ``EntityManager`` will follow configured cascades, providing a better +memory management since associations will be garbage collected, optimizing +resources consumption on long running jobs. + +## BC BREAK: NamingStrategy interface changes + +1. A new method ``embeddedFieldToColumnName($propertyName, $embeddedColumnName)`` + +This method generates the column name for fields of embedded objects. If you implement your custom NamingStrategy, you +now also need to implement this new method. + +2. A change to method ``joinColumnName()`` to include the $className + +## Updates on entities scheduled for deletion are no longer processed + +In Doctrine 2.4, if you modified properties of an entity scheduled for deletion, UnitOfWork would +produce an UPDATE statement to be executed right before the DELETE statement. The entity in question +was therefore present in ``UnitOfWork#entityUpdates``, which means that ``preUpdate`` and ``postUpdate`` +listeners were (quite pointlessly) called. In ``preFlush`` listeners, it used to be possible to undo +the scheduled deletion for updated entities (by calling ``persist()`` if the entity was found in both +``entityUpdates`` and ``entityDeletions``). This does not work any longer, because the entire changeset +calculation logic is optimized away. + +## Minor BC BREAK: Default lock mode changed from LockMode::NONE to null in method signatures + +A misconception concerning default lock mode values in method signatures lead to unexpected behaviour +in SQL statements on SQL Server. With a default lock mode of ``LockMode::NONE`` throughout the +method signatures in ORM, the table lock hint ``WITH (NOLOCK)`` was appended to all locking related +queries by default. This could result in unpredictable results because an explicit ``WITH (NOLOCK)`` +table hint tells SQL Server to run a specific query in transaction isolation level READ UNCOMMITTED +instead of the default READ COMMITTED transaction isolation level. +Therefore there now is a distinction between ``LockMode::NONE`` and ``null`` to be able to tell +Doctrine whether to add table lock hints to queries by intention or not. To achieve this, the following +method signatures have been changed to declare ``$lockMode = null`` instead of ``$lockMode = LockMode::NONE``: + +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#load()`` +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#refresh()`` +- ``Doctrine\ORM\Decorator\EntityManagerDecorator#find()`` +- ``Doctrine\ORM\EntityManager#find()`` +- ``Doctrine\ORM\EntityRepository#find()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#load()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#refresh()`` +- ``Doctrine\ORM\Persisters\EntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Persisters\EntityPersister#load()`` +- ``Doctrine\ORM\Persisters\EntityPersister#refresh()`` +- ``Doctrine\ORM\Persisters\JoinedSubclassPersister#getSelectSQL()`` + +You should update signatures for these methods if you have subclassed one of the above classes. +Please also check the calling code of these methods in your application and update if necessary. + +**Note:** +This in fact is really a minor BC BREAK and should not have any affect on database vendors +other than SQL Server because it is the only one that supports and therefore cares about +``LockMode::NONE``. It's really just a FIX for SQL Server environments using ORM. + +## Minor BC BREAK: `__clone` method not called anymore when entities are instantiated via metadata API + +As of PHP 5.6, instantiation of new entities is deferred to the +[`doctrine/instantiator`](https://github.com/doctrine/instantiator) library, which will avoid calling `__clone` +or any public API on instantiated objects. + +## BC BREAK: `Doctrine\ORM\Repository\DefaultRepositoryFactory` is now `final` + +Please implement the `Doctrine\ORM\Repository\RepositoryFactory` interface instead of extending +the `Doctrine\ORM\Repository\DefaultRepositoryFactory`. + +## BC BREAK: New object expression DQL queries now respects user provided aliasing and not return consumed fields + +When executing DQL queries with new object expressions, instead of returning DTOs numerically indexes, it will now respect user provided aliases. Consider the following query: + + SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true + +Previously, your result would be similar to this: + + array( + 0=>array( + 0=>{UserDTO object}, + 1=>{AddressDTO object}, + 2=>{u.id scalar}, + 3=>{u.name scalar}, + 4=>{a.street scalar}, + 5=>{a.postalCode scalar}, + 'addressId'=>{a.id scalar}, + ), + ... + ) + +From now on, the resultset will look like this: + + array( + 0=>array( + 'user'=>{UserDTO object}, + 'address'=>{AddressDTO object}, + 'addressId'=>{a.id scalar} + ), + ... + ) + +## Minor BC BREAK: added second parameter $indexBy in EntityRepository#createQueryBuilder method signature + +Added way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder() + +# Upgrade to 2.4 + +## BC BREAK: Compatibility Bugfix in PersistentCollection#matching() + +In Doctrine 2.3 it was possible to use the new ``matching($criteria)`` +functionality by adding constraints for assocations based on ID: + + Criteria::expr()->eq('association', $assocation->getId()); + +This functionality does not work on InMemory collections however, because +in memory criteria compares object values based on reference. +As of 2.4 the above code will throw an exception. You need to change +offending code to pass the ``$assocation`` reference directly: + + Criteria::expr()->eq('association', $assocation); + +## Composer is now the default autoloader + +The test suite now runs with composer autoloading. Support for PEAR, and tarball autoloading is deprecated. +Support for GIT submodules is removed. + +## OnFlush and PostFlush event always called + +Before 2.4 the postFlush and onFlush events were only called when there were +actually entities that changed. Now these events are called no matter if there +are entities in the UoW or changes are found. + +## Parenthesis are now considered in arithmetic expression + +Before 2.4 parenthesis are not considered in arithmetic primary expression. +That's conceptually wrong, since it might result in wrong values. For example: + +The DQL: + + SELECT 100 / ( 2 * 2 ) FROM MyEntity + +Before 2.4 it generates the SQL: + + SELECT 100 / 2 * 2 FROM my_entity + +Now parenthesis are considered, the previous DQL will generate: + + SELECT 100 / (2 * 2) FROM my_entity + +# Upgrade to 2.3 + +## Auto Discriminator Map breaks userland implementations with Listener + +The new feature to detect discriminator maps automatically when none +are provided breaks userland implementations doing this with a +listener in ``loadClassMetadata`` event. + +## EntityManager#find() not calls EntityRepository#find() anymore + +Previous to 2.3, calling ``EntityManager#find()`` would be delegated to +``EntityRepository#find()``. This has lead to some unexpected behavior in the +core of Doctrine when people have overwritten the find method in their +repositories. That is why this behavior has been reversed in 2.3, and +``EntityRepository#find()`` calls ``EntityManager#find()`` instead. + +## EntityGenerator add*() method generation + +When generating an add*() method for a collection the EntityGenerator will now not +use the Type-Hint to get the singular for the collection name, but use the field-name +and strip a trailing "s" character if there is one. + +## Merge copies non persisted properties too + +When merging an entity in UoW not only mapped properties are copied, but also others. + +## Query, QueryBuilder and NativeQuery parameters *BC break* + +From now on, parameters in queries is an ArrayCollection instead of a simple array. +This affects heavily the usage of setParameters(), because it will not append anymore +parameters to query, but will actually override the already defined ones. +Whenever you are retrieving a parameter (ie. $query->getParameter(1)), you will +receive an instance of Query\Parameter, which contains the methods "getName", +"getValue" and "getType". Parameters are also only converted to when necessary, and +not when they are set. + +Also, related functions were affected: + +* execute($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance +* iterate($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance +* setParameters($parameters) the argument $parameters can be either an key=>value array or an ArrayCollection instance +* getParameters() now returns ArrayCollection instead of array +* getParameter($key) now returns Parameter instance instead of parameter value + +## Query TreeWalker method renamed + +Internal changes were made to DQL and SQL generation. If you have implemented your own TreeWalker, +you probably need to update it. The method walkJoinVariableDeclaration is now named walkJoin. + +## New methods in TreeWalker interface *BC break* + +Two methods getQueryComponents() and setQueryComponent() were added to the TreeWalker interface and all its implementations +including TreeWalkerAdapter, TreeWalkerChain and SqlWalker. If you have your own implementation not inheriting from one of the +above you must implement these new methods. + +## Metadata Drivers + +Metadata drivers have been rewritten to reuse code from Doctrine\Common. Anyone who is using the +`Doctrine\ORM\Mapping\Driver\Driver` interface should instead refer to +`Doctrine\Common\Persistence\Mapping\Driver\MappingDriver`. Same applies to +`Doctrine\ORM\Mapping\Driver\AbstractFileDriver`: you should now refer to +`Doctrine\Common\Persistence\Mapping\Driver\FileDriver`. + +Also, following mapping drivers have been deprecated, please use their replacements in Doctrine\Common as listed: + + * `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain` + * `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\PHPDriver` + * `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver` + +# Upgrade to 2.2 + +## ResultCache implementation rewritten + +The result cache is completely rewritten and now works on the database result level, not inside the ORM AbstractQuery +anymore. This means that for result cached queries the hydration will now always be performed again, regardless of +the hydration mode. Affected areas are: + +1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork + leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore. +2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result. + +The API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now +deprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\DBAL\Cache\QueryCacheProfile` +instance with access to result cache driver, lifetime and cache key. + + +## EntityManager#getPartialReference() creates read-only entity + +Entities returned from EntityManager#getPartialReference() are now marked as read-only if they +haven't been in the identity map before. This means objects of this kind never lead to changes +in the UnitOfWork. + + +## Fields omitted in a partial DQL query or a native query are never updated + +Fields of an entity that are not returned from a partial DQL Query or native SQL query +will never be updated through an UPDATE statement. + + +## Removed support for onUpdate in @JoinColumn + +The onUpdate foreign key handling makes absolutely no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed. + + +## Changes in Annotation Handling + +There have been some changes to the annotation handling in Common 2.2 again, that affect how people with old configurations +from 2.0 have to configure the annotation driver if they don't use `Configuration::newDefaultAnnotationDriver()`: + + // Register the ORM Annotations in the AnnotationRegistry + AnnotationRegistry::registerFile('path/to/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'); + + $reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader(); + $reader->addNamespace('Doctrine\ORM\Mapping'); + $reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache()); + + $driver = new AnnotationDriver($reader, (array)$paths); + + $config->setMetadataDriverImpl($driver); + + +## Scalar mappings can now be omitted from DQL result + +You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore. +Example: + +SELECT u, SUM(a.id) AS HIDDEN numArticles FROM User u LEFT JOIN u.Articles a ORDER BY numArticles DESC HAVING numArticles > 10 + +Your result will be a collection of Users, and not an array with key 0 as User object instance and "numArticles" as the number of articles per user + + +## Map entities as scalars in DQL result + +When hydrating to array or even a mixed result in object hydrator, previously you had the 0 index holding you entity instance. +You are now allowed to alias this, providing more flexibility for you code. +Example: + +SELECT u AS user FROM User u + +Will now return a collection of arrays with index "user" pointing to the User object instance. + + +## Performance optimizations + +Thousands of lines were completely reviewed and optimized for best performance. +Removed redundancy and improved code readability made now internal Doctrine code easier to understand. +Also, Doctrine 2.2 now is around 10-15% faster than 2.1. + +## EntityManager#find(null) + +Previously EntityManager#find(null) returned null. It now throws an exception. + +# Upgrade to 2.1 + +## Interface for EntityRepository + +The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface. + +## 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: + + // new call to the AnnotationRegistry + \Doctrine\Common\Annotations\AnnotationRegistry::registerFile('/doctrine-src/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'); + + $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() + ); + +This is already done inside the ``$config->newDefaultAnnotationDriver``, so everything should automatically work if you are using this method. You can verify if everything still works by executing a console command such as schema-validate that loads all metadata into memory. + +# Update from 2.0-BETA3 to 2.0-BETA4 + +## XML Driver element demoted to attribute + +We changed how the XML Driver allows to define the change-tracking-policy. The working case is now: + + + +# Update from 2.0-BETA2 to 2.0-BETA3 + +## Serialization of Uninitialized Proxies + +As of Beta3 you can now serialize uninitialized proxies, an exception will only be thrown when +trying to access methods on the unserialized proxy as long as it has not been re-attached to the +EntityManager using `EntityManager#merge()`. See this example: + + $proxy = $em->getReference('User', 1); + + $serializedProxy = serialize($proxy); + $detachedProxy = unserialized($serializedProxy); + + echo $em->contains($detachedProxy); // FALSE + + try { + $detachedProxy->getId(); // uninitialized detached proxy + } catch(Exception $e) { + + } + $attachedProxy = $em->merge($detachedProxy); + echo $attackedProxy->getId(); // works! + +## Changed SQL implementation of Postgres and Oracle DateTime types + +The DBAL Type "datetime" included the Timezone Offset in both Postgres and Oracle. As of this version they are now +generated without Timezone (TIMESTAMP WITHOUT TIME ZONE instead of TIMESTAMP WITH TIME ZONE). +See [this comment to Ticket DBAL-22](http://www.doctrine-project.org/jira/browse/DBAL-22?focusedCommentId=13396&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_13396) +for more details as well as migration issues for PostgreSQL and Oracle. + +Both Postgres and Oracle will throw Exceptions during hydration of Objects with "DateTime" fields unless migration steps are taken! + +## Removed multi-dot/deep-path expressions in DQL + +The support for implicit joins in DQL through the multi-dot/Deep Path Expressions +was dropped. For example: + + SELECT u FROM User u WHERE u.group.name = ?1 + +See the "u.group.id" here is using multi dots (deep expression) to walk +through the graph of objects and properties. Internally the DQL parser +would rewrite these queries to: + + SELECT u FROM User u JOIN u.group g WHERE g.name = ?1 + +This explicit notation will be the only supported notation as of now. The internal +handling of multi-dots in the DQL Parser was very complex, error prone in edge cases +and required special treatment for several features we added. Additionally +it had edge cases that could not be solved without making the DQL Parser +even much more complex. For this reason we will drop the support for the +deep path expressions to increase maintainability and overall performance +of the DQL parsing process. This will benefit any DQL query being parsed, +even those not using deep path expressions. + +Note that the generated SQL of both notations is exactly the same! You +don't loose anything through this. + +## Default Allocation Size for Sequences + +The default allocation size for sequences has been changed from 10 to 1. This step was made +to not cause confusion with users and also because it is partly some kind of premature optimization. + +# Update from 2.0-BETA1 to 2.0-BETA2 + +There are no backwards incompatible changes in this release. + +# Upgrade from 2.0-ALPHA4 to 2.0-BETA1 + +## EntityRepository deprecates access to protected variables + +Instead of accessing protected variables for the EntityManager in +a custom EntityRepository it is now required to use the getter methods +for all the three instance variables: + +* `$this->_em` now accessible through `$this->getEntityManager()` +* `$this->_class` now accessible through `$this->getClassMetadata()` +* `$this->_entityName` now accessible through `$this->getEntityName()` + +Important: For Beta 2 the protected visibility of these three properties will be +changed to private! + +## Console migrated to Symfony Console + +The Doctrine CLI has been replaced by Symfony Console Configuration + +Instead of having to specify: + + [php] + $cliConfig = new CliConfiguration(); + $cliConfig->setAttribute('em', $entityManager); + +You now have to configure the script like: + + [php] + $helperSet = new \Symfony\Components\Console\Helper\HelperSet(array( + 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + +## Console: No need for Mapping Paths anymore + +In previous versions you had to specify the --from and --from-path options +to show where your mapping paths are from the console. However this information +is already known from the Mapping Driver configuration, so the requirement +for this options were dropped. + +Instead for each console command all the entities are loaded and to +restrict the operation to one or more sub-groups you can use the --filter flag. + +## AnnotationDriver is not a default mapping driver anymore + +In conjunction with the recent changes to Console we realized that the +annotations driver being a default metadata driver lead to lots of glue +code in the console components to detect where entities lie and how to load +them for batch updates like SchemaTool and other commands. However the +annotations driver being a default driver does not really help that much +anyways. + +Therefore we decided to break backwards compatibility in this issue and drop +the support for Annotations as Default Driver and require our users to +specify the driver explicitly (which allows us to ask for the path to all +entities). + +If you are using the annotations metadata driver as default driver, you +have to add the following lines to your bootstrap code: + + $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities")); + $config->setMetadataDriverImpl($driverImpl); + +You have to specify the path to your entities as either string of a single +path or array of multiple paths +to your entities. This information will be used by all console commands to +access all entities. + +Xml and Yaml Drivers work as before! + + +## New inversedBy attribute + +It is now *mandatory* that the owning side of a bidirectional association specifies the +'inversedBy' attribute that points to the name of the field on the inverse side that completes +the association. Example: + + [php] + // BEFORE (ALPHA4 AND EARLIER) + class User + { + //... + /** @OneToOne(targetEntity="Address", mappedBy="user") */ + private $address; + //... + } + class Address + { + //... + /** @OneToOne(targetEntity="User") */ + private $user; + //... + } + + // SINCE BETA1 + // User class DOES NOT CHANGE + class Address + { + //... + /** @OneToOne(targetEntity="User", inversedBy="address") */ + private $user; + //... + } + +Thus, the inversedBy attribute is the counterpart to the mappedBy attribute. This change +was necessary to enable some simplifications and further performance improvements. We +apologize for the inconvenience. + +## Default Property for Field Mappings + +The "default" option for database column defaults has been removed. If desired, database column defaults can +be implemented by using the columnDefinition attribute of the @Column annotation (or the appropriate XML and YAML equivalents). +Prefer PHP default values, if possible. + +## Selecting Partial Objects + +Querying for partial objects now has a new syntax. The old syntax to query for partial objects +now has a different meaning. This is best illustrated by an example. If you previously +had a DQL query like this: + + [sql] + SELECT u.id, u.name FROM User u + +Since BETA1, simple state field path expressions in the select clause are used to select +object fields as plain scalar values (something that was not possible before). +To achieve the same result as previously (that is, a partial object with only id and name populated) +you need to use the following, explicit syntax: + + [sql] + SELECT PARTIAL u.{id,name} FROM User u + +## XML Mapping Driver + +The 'inheritance-type' attribute changed to take last bit of ClassMetadata constant names, i.e. +NONE, SINGLE_TABLE, INHERITANCE_TYPE_JOINED + +## YAML Mapping Driver + +The way to specify lifecycle callbacks in YAML Mapping driver was changed to allow for multiple callbacks +per event. The Old syntax ways: + + [yaml] + lifecycleCallbacks: + doStuffOnPrePersist: prePersist + doStuffOnPostPersist: postPersist + +The new syntax is: + + [yaml] + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + postPersist: [ doStuffOnPostPersist ] + +## PreUpdate Event Listeners + +Event Listeners listening to the 'preUpdate' event can only affect the primitive values of entity changesets +by using the API on the `PreUpdateEventArgs` instance passed to the preUpdate listener method. Any changes +to the state of the entitys properties won't affect the database UPDATE statement anymore. This gives drastic +performance benefits for the preUpdate event. + +## Collection API + +The Collection interface in the Common package has been updated with some missing methods +that were present only on the default implementation, ArrayCollection. Custom collection +implementations need to be updated to adhere to the updated interface. + +# Upgrade from 2.0-ALPHA3 to 2.0-ALPHA4 + +## CLI Controller changes + +CLI main object changed its name and namespace. Renamed from Doctrine\ORM\Tools\Cli to Doctrine\Common\Cli\CliController. +Doctrine\Common\Cli\CliController now only deals with namespaces. Ready to go, Core, Dbal and Orm are available and you can subscribe new tasks by retrieving the namespace and including new task. Example: + + [php] + $cli->getNamespace('Core')->addTask('my-example', '\MyProject\Tools\Cli\Tasks\MyExampleTask'); + + +## CLI Tasks documentation + +Tasks have implemented a new way to build documentation. Although it is still possible to define the help manually by extending the basicHelp and extendedHelp, they are now optional. +With new required method AbstractTask::buildDocumentation, its implementation defines the TaskDocumentation instance (accessible through AbstractTask::getDocumentation()), basicHelp and extendedHelp are now not necessary to be implemented. + +## Changes in Method Signatures + + * A bunch of Methods on both Doctrine\DBAL\Platforms\AbstractPlatform and Doctrine\DBAL\Schema\AbstractSchemaManager + have changed quite significantly by adopting the new Schema instance objects. + +## Renamed Methods + + * Doctrine\ORM\AbstractQuery::setExpireResultCache() -> expireResultCache() + * Doctrine\ORM\Query::setExpireQueryCache() -> expireQueryCache() + +## SchemaTool Changes + + * "doctrine schema-tool --drop" now always drops the complete database instead of + only those tables defined by the current database model. The previous method had + problems when foreign keys of orphaned tables pointed to tables that were scheduled + for deletion. + * Use "doctrine schema-tool --update" to get a save incremental update for your + database schema without deleting any unused tables, sequences or foreign keys. + * Use "doctrine schema-tool --complete-update" to do a full incremental update of + your schema. +# Upgrade from 2.0-ALPHA2 to 2.0-ALPHA3 + +This section details the changes made to Doctrine 2.0-ALPHA3 to make it easier for you +to upgrade your projects to use this version. + +## CLI Changes + +The $args variable used in the cli-config.php for configuring the Doctrine CLI has been renamed to $globalArguments. + +## Proxy class changes + +You are now required to make supply some minimalist configuration with regards to proxy objects. That involves 2 new configuration options. First, the directory where generated proxy classes should be placed needs to be specified. Secondly, you need to configure the namespace used for proxy classes. The following snippet shows an example: + + [php] + // step 1: configure directory for proxy classes + // $config instanceof Doctrine\ORM\Configuration + $config->setProxyDir('/path/to/myproject/lib/MyProject/Generated/Proxies'); + $config->setProxyNamespace('MyProject\Generated\Proxies'); + +Note that proxy classes behave exactly like any other classes when it comes to class loading. Therefore you need to make sure the proxy classes can be loaded by some class loader. If you place the generated proxy classes in a namespace and directory under your projects class files, like in the example above, it would be sufficient to register the MyProject namespace on a class loader. Since the proxy classes are contained in that namespace and adhere to the standards for class loading, no additional work is required. +Generating the proxy classes into a namespace within your class library is the recommended setup. + +Entities with initialized proxy objects can now be serialized and unserialized properly from within the same application. + +For more details refer to the Configuration section of the manual. + +## Removed allowPartialObjects configuration option + +The allowPartialObjects configuration option together with the `Configuration#getAllowPartialObjects` and `Configuration#setAllowPartialObjects` methods have been removed. +The new behavior is as if the option were set to FALSE all the time, basically disallowing partial objects globally. However, you can still use the `Query::HINT_FORCE_PARTIAL_LOAD` query hint to force a query to return partial objects for optimization purposes. + +## Renamed Methods + +* Doctrine\ORM\Configuration#getCacheDir() to getProxyDir() +* Doctrine\ORM\Configuration#setCacheDir($dir) to setProxyDir($dir) diff --git a/vendor/doctrine/orm/bin/doctrine b/vendor/doctrine/orm/bin/doctrine new file mode 100644 index 00000000000..c359ca7d83e --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine @@ -0,0 +1,4 @@ +#!/usr/bin/env php +. + */ + +require_once 'Doctrine/Common/ClassLoader.php'; + +$classLoader = new \Doctrine\Common\ClassLoader('Doctrine'); +$classLoader->register(); + +$classLoader = new \Doctrine\Common\ClassLoader('Symfony'); +$classLoader->register(); + +$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php'; + +$helperSet = null; +if (file_exists($configFile)) { + if ( ! is_readable($configFile)) { + trigger_error( + 'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR + ); + } + + require $configFile; + + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); + +\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet); diff --git a/vendor/doctrine/orm/bin/doctrine.bat b/vendor/doctrine/orm/bin/doctrine.bat new file mode 100644 index 00000000000..a9e8ceefd9e --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine.bat @@ -0,0 +1,9 @@ +@echo off + +if "%PHPBIN%" == "" set PHPBIN=@php_bin@ +if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH +GOTO RUN +:USE_PEAR_PATH +set PHPBIN=%PHP_PEAR_PHP_BIN% +:RUN +"%PHPBIN%" "@bin_dir@\doctrine" %* diff --git a/vendor/doctrine/orm/bin/doctrine.php b/vendor/doctrine/orm/bin/doctrine.php new file mode 100644 index 00000000000..842c5493f8e --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine.php @@ -0,0 +1,66 @@ +. + */ + +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\ORM\Tools\Console\ConsoleRunner; + +$autoloadFiles = array(__DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php'); + +foreach ($autoloadFiles as $autoloadFile) { + if (file_exists($autoloadFile)) { + require_once $autoloadFile; + } +} + +$directories = array(getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config'); + +$configFile = null; +foreach ($directories as $directory) { + $configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php'; + + if (file_exists($configFile)) { + break; + } +} + +if ( ! file_exists($configFile)) { + ConsoleRunner::printCliConfigTemplate(); + exit(1); +} + +if ( ! is_readable($configFile)) { + echo 'Configuration file [' . $configFile . '] does not have read permission.' . "\n"; + exit(1); +} + +$commands = array(); + +$helperSet = require $configFile; + +if ( ! ($helperSet instanceof HelperSet)) { + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet, $commands); diff --git a/vendor/doctrine/orm/composer.json b/vendor/doctrine/orm/composer.json new file mode 100644 index 00000000000..ce740f88a1e --- /dev/null +++ b/vendor/doctrine/orm/composer.json @@ -0,0 +1,47 @@ +{ + "name": "doctrine/orm", + "type": "library", + "description": "Object-Relational-Mapper for PHP", + "keywords": ["orm", "database"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "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"} + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.4", + "ext-pdo": "*", + "doctrine/collections": "~1.2", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/cache": "~1.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "symfony/yaml": "~2.3|~3.0", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "autoload": { + "psr-0": { "Doctrine\\ORM\\": "lib/" } + }, + "autoload-dev": { + "psr-0": { "Doctrine\\Tests\\": "tests/" } + }, + "bin": ["bin/doctrine", "bin/doctrine.php"], + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "archive": { + "exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"] + } +} diff --git a/vendor/doctrine/orm/docs/LICENSE.md b/vendor/doctrine/orm/docs/LICENSE.md new file mode 100644 index 00000000000..1bf8659a1b0 --- /dev/null +++ b/vendor/doctrine/orm/docs/LICENSE.md @@ -0,0 +1,363 @@ +The Doctrine2 documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US) + +Creative Commons Legal Code + +Attribution-NonCommercial-ShareAlike 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR + DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY +BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may be + recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed + in Section 1(g) below, which, by reason of the selection and + arrangement of their contents, constitute intellectual creations, in + which the Work is included in its entirety in unmodified form along + with one or more other contributions, each constituting separate and + independent works in themselves, which together are assembled into a + collective whole. A work that constitutes a Collection will not be + considered an Adaptation (as defined above) for the purposes of this + License. + c. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or + other transfer of ownership. + d. "License Elements" means the following high-level license attributes + as selected by Licensor and indicated in the title of this License: + Attribution, Noncommercial, ShareAlike. + e. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + f. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work + or if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + g. "Work" means the literary and/or artistic work offered under the terms + of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, + pamphlet and other writing; a lecture, address, sermon or other work + of the same nature; a dramatic or dramatico-musical work; a + choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving or + lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied + art; an illustration, map, plan, sketch or three-dimensional work + relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the + extent it is protected as a copyrightable work; or a work performed by + a variety or circus performer to the extent it is not otherwise + considered a literary or artistic work. + h. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + i. "Publicly Perform" means to perform public recitations of the Work and + to communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a + place individually chosen by them; to perform the Work to the public + by any means or process and the communication to the public of the + performances of the Work, including by public digital performance; to + broadcast and rebroadcast the Work by any means including signs, + sounds or images. + j. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to + clearly label, demarcate or otherwise identify that changes were made + to the original Work. For example, a translation could be marked "The + original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights in +other media and formats. Subject to Section 8(f), all rights not expressly +granted by Licensor are hereby reserved, including but not limited to the +rights described in Section 4(e). + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms + on the Work that restrict the terms of this License or the ability of + the recipient of the Work to exercise the rights granted to that + recipient under the terms of the License. You may not sublicense the + Work. You must keep intact all notices that refer to this License and + to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Work, You may not impose any effective technological + measures on the Work that restrict the ability of a recipient of the + Work from You to exercise the rights granted to that recipient under + the terms of the License. This Section 4(a) applies to the Work as + incorporated in a Collection, but this does not require the Collection + apart from the Work itself to be made subject to the terms of this + License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit + as required by Section 4(d), as requested. If You create an + Adaptation, upon notice from any Licensor You must, to the extent + practicable, remove from the Adaptation any credit as required by + Section 4(d), as requested. + b. You may Distribute or Publicly Perform an Adaptation only under: (i) + the terms of this License; (ii) a later version of this License with + the same License Elements as this License; (iii) a Creative Commons + jurisdiction license (either this or a later license version) that + contains the same License Elements as this License (e.g., + Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License"). + You must include a copy of, or the URI, for Applicable License with + every copy of each Adaptation You Distribute or Publicly Perform. You + may not offer or impose any terms on the Adaptation that restrict the + terms of the Applicable License or the ability of the recipient of the + Adaptation to exercise the rights granted to that recipient under the + terms of the Applicable License. You must keep intact all notices that + refer to the Applicable License and to the disclaimer of warranties + with every copy of the Work as included in the Adaptation You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Adaptation, You may not impose any effective technological + measures on the Adaptation that restrict the ability of a recipient of + the Adaptation from You to exercise the rights granted to that + recipient under the terms of the Applicable License. This Section 4(b) + applies to the Adaptation as incorporated in a Collection, but this + does not require the Collection apart from the Adaptation itself to be + made subject to the terms of the Applicable License. + c. You may not exercise any of the rights granted to You in Section 3 + above in any manner that is primarily intended for or directed toward + commercial advantage or private monetary compensation. The exchange of + the Work for other copyrighted works by means of digital file-sharing + or otherwise shall not be considered to be intended for or directed + toward commercial advantage or private monetary compensation, provided + there is no payment of any monetary compensation in con-nection with + the exchange of copyrighted works. + d. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party + or parties; (ii) the title of the Work if supplied; (iii) to the + extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and, (iv) consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by + this Section 4(d) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of these + credits and in a manner at least as prominent as the credits for the + other contributing authors. For the avoidance of doubt, You may only + use the credit required by this Section for the purpose of attribution + in the manner set out above and, by exercising Your rights under this + License, You may not implicitly or explicitly assert or imply any + connection with, sponsorship or endorsement by the Original Author, + Licensor and/or Attribution Parties, as appropriate, of You or Your + use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution + Parties. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor + reserves the exclusive right to collect such royalties for any + exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor reserves + the exclusive right to collect such royalties for any exercise by + You of the rights granted under this License if Your exercise of + such rights is for a purpose or use which is otherwise than + noncommercial as permitted under Section 4(c) and otherwise waives + the right to collect royalties through any statutory or compulsory + licensing scheme; and, + iii. Voluntary License Schemes. The Licensor reserves the right to + collect royalties, whether individually or, in the event that the + Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License that is for a + purpose or use which is otherwise than noncommercial as permitted + under Section 4(c). + f. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify or + take other derogatory action in relation to the Work which would be + prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE +FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS +AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE +WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT +LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, +ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT +DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED +WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, + the Licensor offers to the recipient a license to the Work on the same + terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 + and the Universal Copyright Convention (as revised on July 24, 1971). + These rights and subject matter take effect in the relevant + jurisdiction in which the License terms are sought to be enforced + according to the corresponding provisions of the implementation of + those treaty provisions in the applicable national law. If the + standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such + additional rights are deemed to be included in the License; this + License is not intended to restrict the license of any rights under + applicable law. + + +Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the + Work is licensed under the CCPL, Creative Commons does not authorize + the use by either party of the trademark "Creative Commons" or any + related trademark or logo of Creative Commons without the prior + written consent of Creative Commons. Any permitted use will be in + compliance with Creative Commons' then-current trademark usage + guidelines, as may be published on its website or otherwise made + available upon request from time to time. For the avoidance of doubt, + this trademark restriction does not form part of this License. + + Creative Commons may be contacted at http://creativecommons.org/. + diff --git a/vendor/doctrine/orm/docs/README.md b/vendor/doctrine/orm/docs/README.md new file mode 100644 index 00000000000..4315116f88a --- /dev/null +++ b/vendor/doctrine/orm/docs/README.md @@ -0,0 +1,8 @@ +# Doctrine ORM Documentation + +## How to Generate + +1. Run ./bin/install-dependencies.sh +2. Run ./bin/generate-docs.sh + +It will generate the documentation into the build directory of the checkout. \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/bin/generate-docs.sh b/vendor/doctrine/orm/docs/bin/generate-docs.sh new file mode 100644 index 00000000000..7d06d2a8dd2 --- /dev/null +++ b/vendor/doctrine/orm/docs/bin/generate-docs.sh @@ -0,0 +1,10 @@ +#!/bin/bash +EXECPATH=`dirname $0` +cd $EXECPATH +cd .. + +rm build -Rf +sphinx-build en build + +sphinx-build -b latex en build/pdf +rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/bin/install-dependencies.sh b/vendor/doctrine/orm/docs/bin/install-dependencies.sh new file mode 100644 index 00000000000..86b3bdff7bf --- /dev/null +++ b/vendor/doctrine/orm/docs/bin/install-dependencies.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo apt-get install python25 python25-dev texlive-full rubber +sudo easy_install pygments +sudo easy_install sphinx \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/en/Makefile b/vendor/doctrine/orm/docs/en/Makefile new file mode 100644 index 00000000000..a6f6fce6205 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/Makefile @@ -0,0 +1,89 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Doctrine2ORM.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Doctrine2ORM.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/vendor/doctrine/orm/docs/en/_exts/configurationblock.py b/vendor/doctrine/orm/docs/en/_exts/configurationblock.py new file mode 100644 index 00000000000..36ca61f5b15 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/_exts/configurationblock.py @@ -0,0 +1,93 @@ +#Copyright (c) 2010 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. + +from docutils.parsers.rst import Directive, directives +from docutils import nodes +from string import upper + +class configurationblock(nodes.General, nodes.Element): + pass + +class ConfigurationBlock(Directive): + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + formats = { + 'html': 'HTML', + 'xml': 'XML', + 'php': 'PHP', + 'yaml': 'YAML', + 'jinja': 'Twig', + 'html+jinja': 'Twig', + 'jinja+html': 'Twig', + 'php+html': 'PHP', + 'html+php': 'PHP', + 'ini': 'INI', + 'php-annotations': 'Annotations', + } + + def run(self): + env = self.state.document.settings.env + + node = nodes.Element() + node.document = self.state.document + self.state.nested_parse(self.content, self.content_offset, node) + + entries = [] + for i, child in enumerate(node): + if isinstance(child, nodes.literal_block): + # add a title (the language name) before each block + #targetid = "configuration-block-%d" % env.new_serialno('configuration-block') + #targetnode = nodes.target('', '', ids=[targetid]) + #targetnode.append(child) + + innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']]) + + para = nodes.paragraph() + para += [innernode, child] + + entry = nodes.list_item('') + entry.append(para) + entries.append(entry) + + resultnode = configurationblock() + resultnode.append(nodes.bullet_list('', *entries)) + + return [resultnode] + +def visit_configurationblock_html(self, node): + self.body.append(self.starttag(node, 'div', CLASS='configuration-block')) + +def depart_configurationblock_html(self, node): + self.body.append('\n') + +def visit_configurationblock_latex(self, node): + pass + +def depart_configurationblock_latex(self, node): + pass + +def setup(app): + app.add_node(configurationblock, + html=(visit_configurationblock_html, depart_configurationblock_html), + latex=(visit_configurationblock_latex, depart_configurationblock_latex)) + app.add_directive('configuration-block', ConfigurationBlock) diff --git a/vendor/doctrine/orm/docs/en/changelog/migration_2_5.rst b/vendor/doctrine/orm/docs/en/changelog/migration_2_5.rst new file mode 100644 index 00000000000..43a40e62cf5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/changelog/migration_2_5.rst @@ -0,0 +1,710 @@ +What is new in Doctrine ORM 2.5? +================================ + +This document describes changes between Doctrine ORM 2.4 and 2.5 (currently in +Beta). It contains a description of all the new features and sections +about behavioral changes and potential backwards compatibility breaks. +Please review this document carefully when updating to Doctrine 2.5. + +First note, that with the ORM 2.5 release we are dropping support +for PHP 5.3. We are enforcing this with Composer, servers without +at least PHP 5.4 will not allow installing Doctrine 2.5. + +New Features and Improvements +----------------------------- + +Events: PostLoad now triggered after associations are loaded +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before Doctrine 2.5 if you had an entity with a ``@PostLoad`` event +defined then Doctrine would trigger listeners after the fields were +loaded, but before assocations are available. + +- `DDC-54 `_ +- `Commit `_ + +Events: Add API to programatically add event listeners to Entity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When developing third party libraries or decoupled applications +it can be interesting to develop an entity listener without knowing +the entities that require this listener. + +You can now attach entity listeners to entities using the +``AttachEntityListenersListener`` class, which is listening to the +``loadMetadata`` event that is fired once for every entity during +metadata generation: + +.. code-block:: php + + addEntityListener( + 'MyProject\Entity\User', 'MyProject\Listener\TimestampableListener', + Events::prePersist, 'onPrePersist' + ); + + $evm->addEventListener(Events::loadClassMetadata, $listener); + + class TimestampableListener + { + public function onPrePersist($event) + { + $entity = $event->getEntity(); + $entity->setCreated(new \DateTime('now')); + } + } + +Embeddedable Objects +~~~~~~~~~~~~~~~~~~~~ + +Doctrine now supports creating multiple PHP objects from one database table +implementing a feature called "Embeddedable Objects". Next to an ``@Entity`` +class you can now define a class that is embeddable into a database table of an +entity using the ``@Embeddable`` annotation. Embeddable objects can never be +saved, updated or deleted on their own, only as part of an entity (called +"root-entity" or "aggregate"). Consequently embeddables don't have a primary +key, they are identified only by their values. + +Example of defining and using embeddables classes: + +.. code-block:: php + + `_. + +This feature was developed by external contributor `Johannes Schmitt +`_ + +- `DDC-93 `_ +- `Pull Request `_ + +Second-Level-Cache +~~~~~~~~~~~~~~~~~~ + +Since version 2.0 of Doctrine, fetching the same object twice by primary key +would result in just one query. This was achieved by the identity map pattern +(first-level-cache) that kept entities in memory. + +The newly introduced second-level-cache works a bit differently. Instead +of saving objects in memory, it saves them in a fast in-memory cache such +as Memcache, Redis, Riak or MongoDB. Additionally it allows saving the result +of more complex queries than by primary key. Summarized this feature works +like the existing Query result cache, but it is much more powerful. + +As an example lets cache an entity Country that is a relation to the User +entity. We always want to display the country, but avoid the additional +query to this table. + +.. code-block:: php + + setSecondLevelCacheEnabled(); + + $cacheConfig = $config->getSecondLevelCacheConfiguration(); + $regionConfig = $cacheConfig->getRegionsConfiguration(); + $regionConfig->setLifetime('country_region', 3600); + +Now Doctrine will first check for the data of any country in the cache +instead of the database. + +- `Documentation + `_ +- `Pull Request `_ + +Criteria API: Support for ManyToMany assocations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We introduced support for querying collections using the `Criteria API +`_ +in 2.4. This only worked efficently for One-To-Many assocations, not for +Many-To-Many. With the start of 2.5 also Many-To-Many associations get queried +instead of loading them into memory. + +Criteria API: Add new contains() expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is now possible to use the Criteria API to check for string contains needle +using ``contains()``. This translates to using a ``column LIKE '%needle%'`` SQL +condition. + +.. code-block:: php + + where(Criteria::expr()->contains('name', 'Benjamin')); + + $users = $repository->matching($criteria); + +Criteria API: Support for EXTRA_LAZY +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A collection that is marked as ``fetch="EXTRA_LAZY"`` will now return another +lazy collection when using ``Collection::matching($criteria)``: + +.. code-block:: php + + where(Criteria->expr()->eq("published", 1)); + + $publishedComments = $post->getComments()->matching($criteria); + + echo count($publishedComments); + +The lazy criteria currently supports the ``count()`` and ``contains()`` +functionality lazily. All other operations of the ``Collection`` interface +trigger a full load of the collection. + +This feature was contributed by `Michaël Gallego `_. + +- `Pull Request #1 `_ +- `Pull Request #2 `_ + +Mapping: Allow configuring Index flags +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is now possible to control the index flags in the DBAL +schema abstraction from the ORM using metadata. This was possible +only with a schema event listener before. + +.. code-block:: php + + `_. + +- `Pull Request `_ + +SQLFilter API: Check if a parameter is set +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can now check in your SQLFilter if a parameter was set. This allows +to more easily control which features of a filter to enable or disable. + +Extending on the locale example of the documentation: + +.. code-block:: php + + reflClass->implementsInterface('LocaleAware')) { + return ""; + } + + if (!$this->hasParameter('locale')) { + return ""; + } + + return $targetTableAlias.'.locale = ' . $this->getParameter('locale'); + } + } + +This feature was contributed by `Miroslav Demovic `_ + +- `Pull Request `_ + + +EXTRA_LAZY Improvements +~~~~~~~~~~~~~~~~~~~~~~~ + +1. Efficient query when using EXTRA_LAZY and containsKey + + When calling ``Collection::containsKey($key)`` on one-to-many and many-to-many + collections using ``indexBy`` and ``EXTRA_LAZY`` a query is now executed to check + for the existance for the item. Prevoiusly this operation was performed in memory + by loading all entities of the collection. + + .. code-block:: php + + getGroups()->containsKey($groupId)) { + echo "User is in group $groupId\n"; + } + + This feature was contributed by `Asmir Mustafic `_ + + - `Pull Request `_ + +2. Add EXTRA_LAZY Support for get() for owning and inverse many-to-many + + This was contributed by `Sander Marechal `_. + +Improve efficiency of One-To-Many EAGER +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When marking a one-to-many association with ``fetch="EAGER"`` it will now +execute one query less than before and work correctly in combination with +``indexBy``. + +Better support for EntityManagerInterface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Many of the locations where previously only the ``Doctrine\ORM\EntityManager`` +was allowed are now changed to accept the ``EntityManagerInterface`` that was +introduced in 2.4. This allows you to more easily use the decorator pattern +to extend the EntityManager if you need. It's still not replaced everywhere, +so you still have to be careful. + +DQL Improvements +~~~~~~~~~~~~~~~~ + +1. It is now possible to add functions to the ``ORDER BY`` clause in DQL statements: + +.. code-block:: php + + createQuery($dql); + $query->setParameter('groups', array(1, 2, 3)); + + $users = $query->getResult(); + +6. Expressions inside ``COUNT()`` now allowed + +.. code-block:: php + + `_ to pass a callback instead that resolves +the function: + +.. code-block:: php + + addCustomNumericFunction( + 'IS_PUBLISHED', function($funcName) use ($currentSiteId) { + return new IsPublishedFunction($currentSiteId); + } + ); + +Query API: WHERE IN Query using a Collection as parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When performing a ``WHERE IN`` query for a collection of entities you can +now pass the array collection of entities as a parameter value to the query +object: + +.. code-block:: php + + getChildren(); + + $queryBuilder + ->select('p') + ->from('Product', 'p') + ->where('p.category IN (:categories)') + ->setParameter('categories', $categories) + ; + +This feature was contributed by `Michael Perrin +`_. + +- `Pull Request `_ +- `DDC-2319 `_ + +Query API: Add suport for default Query Hints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To configure multiple different features such as custom AST Walker, fetch modes, +locking and other features affecting DQL generation we have had a feature +called "query hints" since version 2.0. + +It is now possible to add query hints that are always enabled for every Query: + +.. code-block:: php + + setDefaultQueryHints( + 'doctrine.customOutputWalker' => 'MyProject\CustomOutputWalker' + ); + +This feature was contributed by `Artur Eshenbrener +`_. + +- `Pull Request `_ + +ResultSetMappingBuilder: Add support for Single-Table Inheritance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before 2.5 the ResultSetMappingBuilder did not work with entities +that are using Single-Table-Inheritance. This restriction was lifted +by adding the missing support. + +YAML Mapping: Many-To-Many doesnt require join column definition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In Annotations and XML it was not necessary using conventions for naming +the many-to-many join column names, in YAML it was not possible however. + +A many-to-many definition in YAML is now possible using this minimal +definition: + +.. code-block:: yaml + + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + +Schema Validator Command: Allow to skip sub-checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Schema Validator command executes two independent checks +for validity of the mappings and if the schema is synchronized +correctly. It is now possible to skip any of the two steps +when executing the command: + +:: + + $ php vendor/bin/doctrine orm:validate-schema --skip-mapping + $ php vendor/bin/doctrine orm:validate-schema --skip-sync + +This allows you to write more specialized continuous integration and automation +checks. When no changes are found the command returns the exit code 0 +and 1, 2 or 3 when failing because of mapping, sync or both. + +EntityGenerator Command: Avoid backups +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When calling the EntityGenerator for an existing entity, Doctrine would +create a backup file every time to avoid loosing changes to the code. +You can now skip generating the backup file by passing the ``--no-backup`` +flag: + +:: + + $ php vendor/bin/doctrine orm:generate-entities src/ --no-backup + +Support for Objects as Identifiers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is now possible to use Objects as identifiers for Entities +as long as they implement the magic method ``__toString()``. + +.. code-block:: php + + value = $value; + } + + public function __toString() + { + return (string)$this->value; + } + } + + class User + { + /** @Id @Column(type="userid") */ + private $id; + + public function __construct(UserId $id) + { + $this->id = $id; + } + } + + class UserIdType extends \Doctrine\DBAL\Types\Type + { + // ... + } + + Doctrine\DBAL\Types\Type::addType('userid', 'MyProject\UserIdType'); + +Behavioral Changes (BC Breaks) +------------------------------ + +NamingStrategy interface changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Doctrine\ORM\Mapping\NamingStrategyInterface`` changed slightly +to pass the Class Name of the entity into the join column name generation: + +:: + + - function joinColumnName($propertyName); + + function joinColumnName($propertyName, $className = null); + +It also received a new method for supporting embeddables: + +:: + + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName); + +Minor BC BREAK: EntityManagerInterface instead of EntityManager in type-hints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require +an ``EntityManagerInterface`` instead. +If you are extending any of the following classes, then you need to check following +signatures: + +- ``Doctrine\ORM\Tools\DebugUnitOfWorkListener#dumpIdentityMap(EntityManagerInterface $em)`` +- ``Doctrine\ORM\Mapping\ClassMetadataFactory#setEntityManager(EntityManagerInterface $em)`` + +Minor BC BREAK: Custom Hydrators API change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of 2.5, ``AbstractHydrator`` does not enforce the usage of cache as part of +API, and now provides you a clean API for column information through the method +``hydrateColumnInfo($column)``. +Cache variable being passed around by reference is no longer needed since +Hydrators are per query instantiated since Doctrine 2.4. + +- `DDC-3060 `_ + +Minor BC BREAK: All non-transient classes in an inheritance must be part of the inheritance map +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of 2.5, classes, if you define an inheritance map for an inheritance tree, you are required +to map all non-transient classes in that inheritance, including the root of the inheritance. + +So far, the root of the inheritance was allowed to be skipped in the inheritance map: this is +not possible anymore, and if you don't plan to persist instances of that class, then you should +either: + +- make that class as ``abstract`` +- add that class to your inheritance map + +If you fail to do so, then a ``Doctrine\ORM\Mapping\MappingException`` will be thrown. + + +- `DDC-3300 `_ +- `DDC-3503 `_ + +Minor BC BREAK: Entity based EntityManager#clear() calls follow cascade detach +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whenever ``EntityManager#clear()`` method gets called with a given entity class +name, until 2.4, it was only detaching the specific requested entity. +As of 2.5, ``EntityManager`` will follow configured cascades, providing a better +memory management since associations will be garbage collected, optimizing +resources consumption on long running jobs. + +Updates on entities scheduled for deletion are no longer processed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In Doctrine 2.4, if you modified properties of an entity scheduled for deletion, UnitOfWork would +produce an ``UPDATE`` statement to be executed right before the ``DELETE`` statement. The entity in question +was therefore present in ``UnitOfWork#entityUpdates``, which means that ``preUpdate`` and ``postUpdate`` +listeners were (quite pointlessly) called. In ``preFlush`` listeners, it used to be possible to undo +the scheduled deletion for updated entities (by calling ``persist()`` if the entity was found in both +``entityUpdates`` and ``entityDeletions``). This does not work any longer, because the entire changeset +calculation logic is optimized away. + +Minor BC BREAK: Default lock mode changed from LockMode::NONE to null in method signatures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A misconception concerning default lock mode values in method signatures lead to unexpected behaviour +in SQL statements on SQL Server. With a default lock mode of ``LockMode::NONE`` throughout the +method signatures in ORM, the table lock hint ``WITH (NOLOCK)`` was appended to all locking related +queries by default. This could result in unpredictable results because an explicit ``WITH (NOLOCK)`` +table hint tells SQL Server to run a specific query in transaction isolation level READ UNCOMMITTED +instead of the default READ COMMITTED transaction isolation level. +Therefore there now is a distinction between ``LockMode::NONE`` and ``null`` to be able to tell +Doctrine whether to add table lock hints to queries by intention or not. To achieve this, the following +method signatures have been changed to declare ``$lockMode = null`` instead of ``$lockMode = LockMode::NONE``: + +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#load()`` +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#refresh()`` +- ``Doctrine\ORM\Decorator\EntityManagerDecorator#find()`` +- ``Doctrine\ORM\EntityManager#find()`` +- ``Doctrine\ORM\EntityRepository#find()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#load()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#refresh()`` +- ``Doctrine\ORM\Persisters\EntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Persisters\EntityPersister#load()`` +- ``Doctrine\ORM\Persisters\EntityPersister#refresh()`` +- ``Doctrine\ORM\Persisters\JoinedSubclassPersister#getSelectSQL()`` + +You should update signatures for these methods if you have subclassed one of the above classes. +Please also check the calling code of these methods in your application and update if necessary. + +.. note:: + + This in fact is really a minor BC BREAK and should not have any affect on database vendors + other than SQL Server because it is the only one that supports and therefore cares about + ``LockMode::NONE``. It's really just a FIX for SQL Server environments using ORM. + +Minor BC BREAK: __clone method not called anymore when entities are instantiated via metadata API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of PHP 5.6, instantiation of new entities is deferred to the +`doctrine/instantiator `_ library, which will avoid calling ``__clone`` +or any public API on instantiated objects. + +BC BREAK: DefaultRepositoryFactory is now final +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please implement the ``Doctrine\ORM\Repository\RepositoryFactory`` interface instead of extending +the ``Doctrine\ORM\Repository\DefaultRepositoryFactory``. + +BC BREAK: New object expression DQL queries now respects user provided aliasing and not return consumed fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When executing DQL queries with new object expressions, instead of returning +DTOs numerically indexes, it will now respect user provided aliases. Consider +the following query: + +:: + + SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId + FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true + +Previously, your result would be similar to this: + +:: + + array( + 0=>array( + 0=>{UserDTO object}, + 1=>{AddressDTO object}, + 2=>{u.id scalar}, + 3=>{u.name scalar}, + 4=>{a.street scalar}, + 5=>{a.postalCode scalar}, + 'addressId'=>{a.id scalar}, + ), + ... + ) + +From now on, the resultset will look like this: + +:: + + array( + 0=>array( + 'user'=>{UserDTO object}, + 'address'=>{AddressDTO object}, + 'addressId'=>{a.id scalar} + ), + ... + ) diff --git a/vendor/doctrine/orm/docs/en/conf.py b/vendor/doctrine/orm/docs/en/conf.py new file mode 100644 index 00000000000..5155ac9a6e7 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/conf.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +# +# Doctrine 2 ORM documentation build configuration file, created by +# sphinx-quickstart on Fri Dec 3 18:10:24 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.append(os.path.abspath('_exts')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['configurationblock'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Doctrine 2 ORM' +copyright = u'2010-12, Doctrine Project Team' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '2' +# The full version, including alpha/beta/rc tags. +release = '2' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'doctrine' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['_theme'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Doctrine2ORMdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Doctrine2ORM.tex', u'Doctrine 2 ORM Documentation', + u'Doctrine Project Team', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True + +primary_domain = "dcorm" + +def linkcode_resolve(domain, info): + if domain == 'dcorm': + return 'http://' + return None diff --git a/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst b/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst new file mode 100644 index 00000000000..5bbd2a3b11f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst @@ -0,0 +1,256 @@ +Advanced field value conversion using custom mapping types +========================================================== + +.. sectionauthor:: Jan Sorgalla + +When creating entities, you sometimes have the need to transform field values +before they are saved to the database. In Doctrine you can use Custom Mapping +Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`). + +There are several ways to achieve this: converting the value inside the Type +class, converting the value on the database-level or a combination of both. + +This article describes the third way by implementing the MySQL specific column +type `Point `_. + +The ``Point`` type is part of the `Spatial extension `_ +of MySQL and enables you to store a single location in a coordinate space by +using x and y coordinates. You can use the Point type to store a +longitude/latitude pair to represent a geographic location. + +The entity +---------- + +We create a simple entity with a field ``$point`` which holds a value object +``Point`` representing the latitude and longitude of the position. + +The entity class: + +.. code-block:: php + + point = $point; + } + + /** + * @return \Geo\ValueObject\Point + */ + public function getPoint() + { + return $this->point; + } + + /** + * @param string $address + */ + public function setAddress($address) + { + $this->address = $address; + } + + /** + * @return string + */ + public function getAddress() + { + return $this->address; + } + } + +We use the custom type ``point`` in the ``@Column`` docblock annotation of the +``$point`` field. We will create this custom mapping type in the next chapter. + +The point class: + +.. code-block:: php + + latitude = $latitude; + $this->longitude = $longitude; + } + + /** + * @return float + */ + public function getLatitude() + { + return $this->latitude; + } + + /** + * @return float + */ + public function getLongitude() + { + return $this->longitude; + } + } + +The mapping type +---------------- + +Now we're going to create the ``point`` type and implement all required methods. + +.. code-block:: php + + getLongitude(), $value->getLatitude()); + } + + return $value; + } + + public function canRequireSQLConversion() + { + return true; + } + + public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('AsText(%s)', $sqlExpr); + } + + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('PointFromText(%s)', $sqlExpr); + } + } + +We do a 2-step conversion here. In the first step, we convert the ``Point`` +object into a string representation before saving to the database (in the +``convertToDatabaseValue`` method) and back into an object after fetching the +value from the database (in the ``convertToPHPValue`` method). + +The format of the string representation format is called `Well-known text (WKT) +`_. The advantage of this format +is, that it is both human readable and parsable by MySQL. + +Internally, MySQL stores geometry values in a binary format that is not +identical to the WKT format. So, we need to let MySQL transform the WKT +representation into its internal format. + +This is where the ``convertToPHPValueSQL`` and ``convertToDatabaseValueSQL`` +methods come into play. + +This methods wrap a sql expression (the WKT representation of the Point) into +MySQL functions `PointFromText `_ +and `AsText `_ +which convert WKT strings to and from the internal format of MySQL. + +.. note:: + + When using DQL queries, the ``convertToPHPValueSQL`` and + ``convertToDatabaseValueSQL`` methods only apply to identification variables + and path expressions in SELECT clauses. Expressions in WHERE clauses are + **not** wrapped! + + If you want to use Point values in WHERE clauses, you have to implement a + :doc:`user defined function ` for + ``PointFromText``. + +Example usage +------------- + +.. code-block:: php + + getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('point', 'point'); + + // Store a Location object + use Geo\Entity\Location; + use Geo\ValueObject\Point; + + $location = new Location(); + + $location->setAddress('1600 Amphitheatre Parkway, Mountain View, CA'); + $location->setPoint(new Point(37.4220761, -122.0845187)); + + $em->persist($location); + $em->flush(); + $em->clear(); + + // Fetch the Location object + $query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'"); + $location = $query->getSingleResult(); + + /* @var Geo\ValueObject\Point */ + $point = $location->getPoint(); diff --git a/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst b/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst new file mode 100644 index 00000000000..fdf830323e1 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst @@ -0,0 +1,376 @@ +Aggregate Fields +================ + +.. sectionauthor:: Benjamin Eberlei + +You will often come across the requirement to display aggregate +values of data that can be computed by using the MIN, MAX, COUNT or +SUM SQL functions. For any ORM this is a tricky issue +traditionally. Doctrine 2 offers several ways to get access to +these values and this article will describe all of them from +different perspectives. + +You will see that aggregate fields can become very explicit +features in your domain model and how this potentially complex +business rules can be easily tested. + +An example model +---------------- + +Say you want to model a bank account and all their entries. Entries +into the account can either be of positive or negative money +values. Each account has a credit limit and the account is never +allowed to have a balance below that value. + +For simplicity we live in a world were money is composed of +integers only. Also we omit the receiver/sender name, stated reason +for transfer and the execution date. These all would have to be +added on the ``Entry`` object. + +Our entities look like: + +.. code-block:: php + + no = $no; + $this->maxCredit = $maxCredit; + $this->entries = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** + * @Entity + */ + class Entry + { + /** @Id @GeneratedValue @Column(type="integer") */ + private $id; + + /** + * @ManyToOne(targetEntity="Account", inversedBy="entries") + */ + private $account; + + /** + * @Column(type="integer") + */ + private $amount; + + public function __construct($account, $amount) + { + $this->account = $account; + $this->amount = $amount; + // more stuff here, from/to whom, stated reason, execution date and such + } + + public function getAmount() + { + return $this->amount; + } + } + +Using DQL +--------- + +The Doctrine Query Language allows you to select for aggregate +values computed from fields of your Domain Model. You can select +the current balance of your account by calling: + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $myAccountId) + ->getSingleScalarResult(); + +The ``$em`` variable in this (and forthcoming) example holds the +Doctrine ``EntityManager``. We create a query for the SUM of all +amounts (negative amounts are withdraws) and retrieve them as a +single scalar result, essentially return only the first column of +the first row. + +This approach is simple and powerful, however it has a serious +drawback. We have to execute a specific query for the balance +whenever we need it. + +To implement a powerful domain model we would rather have access to +the balance from our ``Account`` entity during all times (even if +the Account was not persisted in the database before!). + +Also an additional requirement is the max credit per ``Account`` +rule. + +We cannot reliably enforce this rule in our ``Account`` entity with +the DQL retrieval of the balance. There are many different ways to +retrieve accounts. We cannot guarantee that we can execute the +aggregation query for all these use-cases, let alone that a +userland programmer checks this balance against newly added +entries. + +Using your Domain Model +----------------------- + +``Account`` and all the ``Entry`` instances are connected through a +collection, which means we can compute this value at runtime: + +.. code-block:: php + + entries as $entry) { + $balance += $entry->getAmount(); + } + return $balance; + } + } + +Now we can always call ``Account::getBalance()`` to access the +current account balance. + +To enforce the max credit rule we have to implement the "Aggregate +Root" pattern as described in Eric Evans book on Domain Driven +Design. Described with one sentence, an aggregate root controls the +instance creation, access and manipulation of its children. + +In our case we want to enforce that new entries can only added to +the ``Account`` by using a designated method. The ``Account`` is +the aggregate root of this relation. We can also enforce the +correctness of the bi-directional ``Account`` <-> ``Entry`` +relation with this method: + +.. code-block:: php + + assertAcceptEntryAllowed($amount); + + $e = new Entry($this, $amount); + $this->entries[] = $e; + return $e; + } + } + +Now look at the following test-code for our entities: + +.. code-block:: php + + assertEquals(0, $account->getBalance()); + + $account->addEntry(500); + $this->assertEquals(500, $account->getBalance()); + + $account->addEntry(-700); + $this->assertEquals(-200, $account->getBalance()); + } + + public function testExceedMaxLimit() + { + $account = new Account("123456", $maxCredit = 200); + + $this->setExpectedException("Exception"); + $account->addEntry(-1000); + } + } + +To enforce our rule we can now implement the assertion in +``Account::addEntry``: + +.. code-block:: php + + getBalance() + $amount; + $allowedMinimalBalance = ($this->maxCredit * -1); + if ($futureBalance < $allowedMinimalBalance) { + throw new Exception("Credit Limit exceeded, entry is not allowed!"); + } + } + } + +We haven't talked to the entity manager for persistence of our +account example before. You can call +``EntityManager::persist($account)`` and then +``EntityManager::flush()`` at any point to save the account to the +database. All the nested ``Entry`` objects are automatically +flushed to the database also. + +.. code-block:: php + + addEntry(500); + $account->addEntry(-200); + $em->persist($account); + $em->flush(); + +The current implementation has a considerable drawback. To get the +balance, we have to initialize the complete ``Account::$entries`` +collection, possibly a very large one. This can considerably hurt +the performance of your application. + +Using an Aggregate Field +------------------------ + +To overcome the previously mentioned issue (initializing the whole +entries collection) we want to add an aggregate field called +"balance" on the Account and adjust the code in +``Account::getBalance()`` and ``Account:addEntry()``: + +.. code-block:: php + + balance; + } + + public function addEntry($amount) + { + $this->assertAcceptEntryAllowed($amount); + + $e = new Entry($this, $amount); + $this->entries[] = $e; + $this->balance += $amount; + return $e; + } + } + +This is a very simple change, but all the tests still pass. Our +account entities return the correct balance. Now calling the +``Account::getBalance()`` method will not occur the overhead of +loading all entries anymore. Adding a new Entry to the +``Account::$entities`` will also not initialize the collection +internally. + +Adding a new entry is therefore very performant and explicitly +hooked into the domain model. It will only update the account with +the current balance and insert the new entry into the database. + +Tackling Race Conditions with Aggregate Fields +---------------------------------------------- + +Whenever you denormalize your database schema race-conditions can +potentially lead to inconsistent state. See this example: + +.. code-block:: php + + find('Bank\Entities\Account', $accId); + + // request 2 account + $account2 = $em->find('Bank\Entities\Account', $accId); + + $account1->addEntry(-200); + $account2->addEntry(-200); + + // now request 1 and 2 both flush the changes. + +The aggregate field ``Account::$balance`` is now -200, however the +SUM over all entries amounts yields -400. A violation of our max +credit rule. + +You can use both optimistic or pessimistic locking to save-guard +your aggregate fields against this kind of race-conditions. Reading +Eric Evans DDD carefully he mentions that the "Aggregate Root" +(Account in our example) needs a locking mechanism. + +Optimistic locking is as easy as adding a version column: + +.. code-block:: php + + find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ); + +Keeping Updates and Deletes in Sync +----------------------------------- + +The example shown in this article does not allow changes to the +value in ``Entry``, which considerably simplifies the effort to +keep ``Account::$balance`` in sync. If your use-case allows fields +to be updated or related entities to be removed you have to +encapsulate this logic in your "Aggregate Root" entity and adjust +the aggregate field accordingly. + +Conclusion +---------- + +This article described how to obtain aggregate values using DQL or +your domain model. It showed how you can easily add an aggregate +field that offers serious performance benefits over iterating all +the related objects that make up an aggregate value. Finally I +showed how you can ensure that your aggregate fields do not get out +of sync due to race-conditions and concurrent access. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/custom-mapping-types.rst b/vendor/doctrine/orm/docs/en/cookbook/custom-mapping-types.rst new file mode 100644 index 00000000000..1bd4f124215 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/custom-mapping-types.rst @@ -0,0 +1,103 @@ +Custom Mapping Types +==================== + +Doctrine allows you to create new mapping types. This can come in +handy when you're missing a specific mapping type or when you want +to replace the existing implementation of a mapping type. + +In order to create a new mapping type you need to subclass +``Doctrine\DBAL\Types\Type`` and implement/override the methods as +you wish. Here is an example skeleton of such a custom type class: + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype'); + +When registering the custom types in the configuration you specify a unique +name for the mapping type and map that to the corresponding fully qualified +class name. Now the new type can be used when mapping columns: + +.. code-block:: php + + + +This recipe will show you a simple example of how you can use +Doctrine 2 to persist an implementation of the +`Decorator Pattern `_ + +Component +--------- + +The ``Component`` class needs to be persisted, so it's going to +be an ``Entity``. As the top of the inheritance hierarchy, it's going +to have to define the persistent inheritance. For this example, we +will use Single Table Inheritance, but Class Table Inheritance +would work as well. In the discriminator map, we will define two +concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``. + +.. code-block:: php + + id; + } + + /** + * Set name + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Get name + * @return string $name + */ + public function getName() + { + return $this->name; + } + + } + +ConcreteComponent +----------------- + +The ``ConcreteComponent`` class is pretty simple and doesn't do much +more than extend the abstract ``Component`` class (only for the +purpose of keeping this example simple). + +.. code-block:: php + + setDecorates($c); + } + + /** + * (non-PHPdoc) + * @see Test.Component::getName() + */ + public function getName() + { + return 'Decorated ' . $this->getDecorates()->getName(); + } + + /** + * the component being decorated + * @return Component + */ + protected function getDecorates() + { + return $this->decorates; + } + + /** + * sets the component being decorated + * @param Component $c + */ + protected function setDecorates(Component $c) + { + $this->decorates = $c; + } + + } + +All operations on the ``Decorator`` (i.e. persist, remove, etc) will +cascade from the ``Decorator`` to the ``Component``. This means that +when we persist a ``Decorator``, Doctrine will take care of +persisting the chain of decorated objects for us. A ``Decorator`` can +be treated exactly as a ``Component`` when it comes time to +persisting it. + +The ``Decorator's`` constructor accepts an instance of a +``Component``, as defined by the ``Decorator`` pattern. The +setDecorates/getDecorates methods have been defined as protected to +hide the fact that a ``Decorator`` is decorating a ``Component`` and +keeps the ``Component`` interface and the ``Decorator`` interface +identical. + +To illustrate the intended result of the ``Decorator`` pattern, the +getName() method has been overridden to append a string to the +``Component's`` getName() method. + +ConcreteDecorator +----------------- + +The final class required to complete a simple implementation of the +Decorator pattern is the ``ConcreteDecorator``. In order to further +illustrate how the ``Decorator`` can alter data as it moves through +the chain of decoration, a new field, "special", has been added to +this class. The getName() has been overridden and appends the value +of the getSpecial() method to its return value. + +.. code-block:: php + + special = $special; + } + + /** + * Get special + * @return string $special + */ + public function getSpecial() + { + return $this->special; + } + + /** + * (non-PHPdoc) + * @see Test.Component::getName() + */ + public function getName() + { + return '[' . $this->getSpecial() + . '] ' . parent::getName(); + } + + } + +Examples +-------- + +Here is an example of how to persist and retrieve your decorated +objects + +.. code-block:: php + + setName('Test Component 1'); + $em->persist($c); // assigned unique ID = 1 + + // create a new concrete decorator + $c = new ConcreteComponent(); + $c->setName('Test Component 2'); + + $d = new ConcreteDecorator($c); + $d->setSpecial('Really'); + $em->persist($d); + // assigns c as unique ID = 2, and d as unique ID = 3 + + $em->flush(); + + $c = $em->find('Test\Component', 1); + $d = $em->find('Test\Component', 3); + + echo get_class($c); + // prints: Test\Component\ConcreteComponent + + echo $c->getName(); + // prints: Test Component 1 + + echo get_class($d) + // prints: Test\Component\ConcreteDecorator + + echo $d->getName(); + // prints: [Really] Decorated Test Component 2 + diff --git a/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst b/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst new file mode 100644 index 00000000000..e840126db38 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst @@ -0,0 +1,217 @@ +Extending DQL in Doctrine 2: Custom AST Walkers +=============================================== + +.. sectionauthor:: Benjamin Eberlei + +The Doctrine Query Language (DQL) is a proprietary sql-dialect that +substitutes tables and columns for Entity names and their fields. +Using DQL you write a query against the database using your +entities. With the help of the metadata you can write very concise, +compact and powerful queries that are then translated into SQL by +the Doctrine ORM. + +In Doctrine 1 the DQL language was not implemented using a real +parser. This made modifications of the DQL by the user impossible. +Doctrine 2 in contrast has a real parser for the DQL language, +which transforms the DQL statement into an +`Abstract Syntax Tree `_ +and generates the appropriate SQL statement for it. Since this +process is deterministic Doctrine heavily caches the SQL that is +generated from any given DQL query, which reduces the performance +overhead of the parsing process to zero. + +You can modify the Abstract syntax tree by hooking into DQL parsing +process by adding a Custom Tree Walker. A walker is an interface +that walks each node of the Abstract syntax tree, thereby +generating the SQL statement. + +There are two types of custom tree walkers that you can hook into +the DQL parser: + + +- An output walker. This one actually generates the SQL, and there + is only ever one of them. We implemented the default SqlWalker + implementation for it. +- A tree walker. There can be many tree walkers, they cannot + generate the sql, however they can modify the AST before its + rendered to sql. + +Now this is all awfully technical, so let me come to some use-cases +fast to keep you motivated. Using walker implementation you can for +example: + + +- Modify the AST to generate a Count Query to be used with a + paginator for any given DQL query. +- Modify the Output Walker to generate vendor-specific SQL + (instead of ANSI). +- Modify the AST to add additional where clauses for specific + entities (example ACL, country-specific content...) +- Modify the Output walker to pretty print the SQL for debugging + purposes. + +In this cookbook-entry I will show examples on the first two +points. There are probably much more use-cases. + +Generic count query for pagination +---------------------------------- + +Say you have a blog and posts all with one category and one author. +A query for the front-page or any archive page might look something +like: + +.. code-block:: sql + + SELECT p, c, a FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ... + +Now in this query the blog post is the root entity, meaning its the +one that is hydrated directly from the query and returned as an +array of blog posts. In contrast the comment and author are loaded +for deeper use in the object tree. + +A pagination for this query would want to approximate the number of +posts that match the WHERE clause of this query to be able to +predict the number of pages to show to the user. A draft of the DQL +query for pagination would look like: + +.. code-block:: sql + + SELECT count(DISTINCT p.id) FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ... + +Now you could go and write each of these queries by hand, or you +can use a tree walker to modify the AST for you. Lets see how the +API would look for this use-case: + +.. code-block:: php + + createQuery($dql); + $query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20); + + $totalResults = Paginate::count($query); + $results = $query->getResult(); + +The ``Paginate::count(Query $query)`` looks like: + +.. code-block:: php + + setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker')); + $countQuery->setFirstResult(null)->setMaxResults(null); + + return $countQuery->getSingleScalarResult(); + } + } + +It clones the query, resets the limit clause first and max results +and registers the ``CountSqlWalker`` custom tree walker which +will modify the AST to execute a count query. The walkers +implementation is: + +.. code-block:: php + + _getQueryComponents() as $dqlAlias => $qComp) { + if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) { + $parent = $qComp; + $parentName = $dqlAlias; + break; + } + } + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, + $parent['metadata']->getSingleIdentifierFieldName() + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + $AST->selectClause->selectExpressions = array( + new SelectExpression( + new AggregateExpression('count', $pathExpression, true), null + ) + ); + } + } + +This will delete any given select expressions and replace them with +a distinct count query for the root entities primary key. This will +only work if your entity has only one identifier field (composite +keys won't work). + +Modify the Output Walker to generate Vendor specific SQL +-------------------------------------------------------- + +Most RMDBS have vendor-specific features for optimizing select +query execution plans. You can write your own output walker to +introduce certain keywords using the Query Hint API. A query hint +can be set via ``Query::setHint($name, $value)`` as shown in the +previous example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint. + +We will implement a custom Output Walker that allows to specify the +SQL\_NO\_CACHE query hint. + +.. code-block:: php + + createQuery($dql); + $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'DoctrineExtensions\Query\MysqlWalker'); + $query->setHint("mysqlWalker.sqlNoCache", true); + $results = $query->getResult(); + +Our ``MysqlWalker`` will extend the default ``SqlWalker``. We will +modify the generation of the SELECT clause, adding the +SQL\_NO\_CACHE on those queries that need it: + +.. code-block:: php + + getQuery()->getHint('mysqlWalker.sqlNoCache') === true) { + if ($selectClause->isDistinct) { + $sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql); + } else { + $sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql); + } + } + + return $sql; + } + } + +Writing extensions to the Output Walker requires a very deep +understanding of the DQL Parser and Walkers, but may offer your +huge benefits with using vendor specific features. This would still +allow you write DQL queries instead of NativeQueries to make use of +vendor specific features. + diff --git a/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst b/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst new file mode 100644 index 00000000000..a3a7606ad80 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst @@ -0,0 +1,251 @@ +DQL User Defined Functions +========================== + +.. sectionauthor:: Benjamin Eberlei + +By default DQL supports a limited subset of all the vendor-specific +SQL functions common between all the vendors. However in many cases +once you have decided on a specific database vendor, you will never +change it during the life of your project. This decision for a +specific vendor potentially allows you to make use of powerful SQL +features that are unique to the vendor. + +It is worth to mention that Doctrine 2 also allows you to handwrite +your SQL instead of extending the DQL parser. Extending DQL is sort of an +advanced extension point. You can map arbitrary SQL to your objects +and gain access to vendor specific functionalities using the +``EntityManager#createNativeQuery()`` API as described in +the :doc:`Native Query <../reference/native-sql>` chapter. + + +The DQL Parser has hooks to register functions that can then be +used in your DQL queries and transformed into SQL, allowing to +extend Doctrines Query capabilities to the vendors strength. This +post explains the Used-Defined Functions API (UDF) of the Dql +Parser and shows some examples to give you some hints how you would +extend DQL. + +There are three types of functions in DQL, those that return a +numerical value, those that return a string and those that return a +Date. Your custom method has to be registered as either one of +those. The return type information is used by the DQL parser to +check possible syntax errors during the parsing process, for +example using a string function return value in a math expression. + +Registering your own DQL functions +---------------------------------- + +You can register your functions adding them to the ORM +configuration: + +.. code-block:: php + + addCustomStringFunction($name, $class); + $config->addCustomNumericFunction($name, $class); + $config->addCustomDatetimeFunction($name, $class); + + $em = EntityManager::create($dbParams, $config); + +The ``$name`` is the name the function will be referred to in the +DQL query. ``$class`` is a string of a class-name which has to +extend ``Doctrine\ORM\Query\Node\FunctionNode``. This is a class +that offers all the necessary API and methods to implement a UDF. + +Instead of providing the function class name, you can also provide +a callable that returns the function object: + +.. code-block:: php + + addCustomStringFunction($name, function () { + return new MyCustomFunction(); + }); + +In this post we will implement some MySql specific Date calculation +methods, which are quite handy in my opinion: + +Date Diff +--------- + +`Mysql's DateDiff function `_ +takes two dates as argument and calculates the difference in days +with ``date1-date2``. + +The DQL parser is a top-down recursive descent parser to generate +the Abstract-Syntax Tree (AST) and uses a TreeWalker approach to +generate the appropriate SQL from the AST. This makes reading the +Parser/TreeWalker code manageable in a finite amount of time. + +The ``FunctionNode`` class I referred to earlier requires you to +implement two methods, one for the parsing process (obviously) +called ``parse`` and one for the TreeWalker process called +``getSql()``. I show you the code for the DateDiff method and +discuss it step by step: + +.. code-block:: php + + match(Lexer::T_IDENTIFIER); // (2) + $parser->match(Lexer::T_OPEN_PARENTHESIS); // (3) + $this->firstDateExpression = $parser->ArithmeticPrimary(); // (4) + $parser->match(Lexer::T_COMMA); // (5) + $this->secondDateExpression = $parser->ArithmeticPrimary(); // (6) + $parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3) + } + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'DATEDIFF(' . + $this->firstDateExpression->dispatch($sqlWalker) . ', ' . + $this->secondDateExpression->dispatch($sqlWalker) . + ')'; // (7) + } + } + +The Parsing process of the DATEDIFF function is going to find two +expressions the date1 and the date2 values, whose AST Node +representations will be saved in the variables of the DateDiff +FunctionNode instance at (1). + +The parse() method has to cut the function call "DATEDIFF" and its +argument into pieces. Since the parser detects the function using a +lookahead the T\_IDENTIFIER of the function name has to be taken +from the stack (2), followed by a detection of the arguments in +(4)-(6). The opening and closing parenthesis have to be detected +also. This happens during the Parsing process and leads to the +generation of a DateDiff FunctionNode somewhere in the AST of the +dql statement. + +The ``ArithmeticPrimary`` method call is the most common +denominator of valid EBNF tokens taken from the +`DQL EBNF grammar `_ +that matches our requirements for valid input into the DateDiff Dql +function. Picking the right tokens for your methods is a tricky +business, but the EBNF grammar is pretty helpful finding it, as is +looking at the Parser source code. + +Now in the TreeWalker process we have to pick up this node and +generate SQL from it, which apparently is quite easy looking at the +code in (7). Since we don't know which type of AST Node the first +and second Date expression are we are just dispatching them back to +the SQL Walker to generate SQL from and then wrap our DATEDIFF +function call around this output. + +Now registering this DateDiff FunctionNode with the ORM using: + +.. code-block:: php + + addCustomStringFunction('DATEDIFF', 'DoctrineExtensions\Query\MySql\DateDiff'); + +We can do fancy stuff like: + +.. code-block:: sql + + SELECT p FROM DoctrineExtensions\Query\BlogPost p WHERE DATEDIFF(CURRENT_TIME(), p.created) < 7 + +Date Add +-------- + +Often useful it the ability to do some simple date calculations in +your DQL query using +`MySql's DATE\_ADD function `_. + +I'll skip the blah and show the code for this function: + +.. code-block:: php + + match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstDateExpression = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_COMMA); + $parser->match(Lexer::T_IDENTIFIER); + + $this->intervalExpression = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_IDENTIFIER); + + /* @var $lexer Lexer */ + $lexer = $parser->getLexer(); + $this->unit = $lexer->token['value']; + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'DATE_ADD(' . + $this->firstDateExpression->dispatch($sqlWalker) . ', INTERVAL ' . + $this->intervalExpression->dispatch($sqlWalker) . ' ' . $this->unit . + ')'; + } + } + +The only difference compared to the DATEDIFF here is, we +additionally need the ``Lexer`` to access the value of the +``T_IDENTIFIER`` token for the Date Interval unit, for example the +MONTH in: + +.. code-block:: sql + + SELECT p FROM DoctrineExtensions\Query\BlogPost p WHERE DATE_ADD(CURRENT_TIME(), INTERVAL 4 MONTH) > p.created + +The above method now only supports the specification using +``INTERVAL``, to also allow a real date in DATE\_ADD we need to add +some decision logic to the parsing process (makes up for a nice +exercise). + +Now as you see, the Parsing process doesn't catch all the possible +SQL errors, here we don't match for all the valid inputs for the +interval unit. However where necessary we rely on the database +vendors SQL parser to show us further errors in the parsing +process, for example if the Unit would not be one of the supported +values by MySql. + +Conclusion +---------- + +Now that you all know how you can implement vendor specific SQL +functionalities in DQL, we would be excited to see user extensions +that add vendor specific function packages, for example more math +functions, XML + GIS Support, Hashing functions and so on. + +For 2.0 we will come with the current set of functions, however for +a future version we will re-evaluate if we can abstract even more +vendor sql functions and extend the DQL languages scope. + +Code for this Extension to DQL and other Doctrine Extensions can be +found +`in my Github DoctrineExtensions repository `_. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst b/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst new file mode 100644 index 00000000000..664cff53f5c --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst @@ -0,0 +1,68 @@ +Entities in the Session +======================= + +There are several use-cases to save entities in the session, for example: + +1. User object +2. Multi-step forms + +To achieve this with Doctrine you have to pay attention to some details to get +this working. + +Merging entity into an EntityManager +------------------------------------ + +In Doctrine an entity objects has to be "managed" by an EntityManager to be +updateable. Entities saved into the session are not managed in the next request +anymore. This means that you have to register these entities with an +EntityManager again if you want to change them or use them as part of +references between other entities. You can achieve this by calling +``EntityManager#merge()``. + +For a representative User object the code to get turn an instance from +the session into a managed Doctrine object looks like this: + +.. code-block:: php + + merge($user); + } + +.. note:: + + A frequent mistake is not to get the merged user object from the return + value of ``EntityManager#merge()``. The entity object passed to merge is + not necessarily the same object that is returned from the method. + +Serializing entity into the session +----------------------------------- + +Entities that are serialized into the session normally contain references to +other entities as well. Think of the user entity has a reference to his +articles, groups, photos or many other different entities. If you serialize +this object into the session then you don't want to serialize the related +entities as well. This is why you should call ``EntityManager#detach()`` on this +object or implement the __sleep() magic method on your entity. + +.. code-block:: php + + find("User", 1); + $em->detach($user); + $_SESSION['user'] = $user; + +.. note:: + + When you called detach on your objects they get "unmanaged" with that + entity manager. This means you cannot use them as part of write operations + during ``EntityManager#flush()`` anymore in this request. + diff --git a/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst b/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst new file mode 100644 index 00000000000..4eb2b717bf5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst @@ -0,0 +1,112 @@ +Implementing ArrayAccess for Domain Objects +=========================================== + +.. sectionauthor:: Roman Borschel (roman@code-factory.org) + +This recipe will show you how to implement ArrayAccess for your +domain objects in order to allow more uniform access, for example +in templates. In these examples we will implement ArrayAccess on a +`Layer Supertype `_ +for all our domain objects. + +Option 1 +-------- + +In this implementation we will make use of PHPs highly dynamic +nature to dynamically access properties of a subtype in a supertype +at runtime. Note that this implementation has 2 main caveats: + + +- It will not work with private fields +- It will not go through any getters/setters + +.. code-block:: php + + $offset); + } + + public function offsetSet($offset, $value) { + $this->$offset = $value; + } + + public function offsetGet($offset) { + return $this->$offset; + } + + public function offsetUnset($offset) { + $this->$offset = null; + } + } + +Option 2 +-------- + +In this implementation we will dynamically invoke getters/setters. +Again we use PHPs dynamic nature to invoke methods on a subtype +from a supertype at runtime. This implementation has the following +caveats: + + +- It relies on a naming convention +- The semantics of offsetExists can differ +- offsetUnset will not work with typehinted setters + +.. code-block:: php + + {"get$offset"}(); + return $value !== null; + } + + public function offsetSet($offset, $value) { + $this->{"set$offset"}($value); + } + + public function offsetGet($offset) { + return $this->{"get$offset"}(); + } + + public function offsetUnset($offset) { + $this->{"set$offset"}(null); + } + } + +Read-only +--------- + +You can slightly tweak option 1 or option 2 in order to make array +access read-only. This will also circumvent some of the caveats of +each option. Simply make offsetSet and offsetUnset throw an +exception (i.e. BadMethodCallException). + +.. code-block:: php + + `_ +for all our domain objects. + +Implementing NotifyPropertyChanged +---------------------------------- + +The NOTIFY policy is based on the assumption that the entities +notify interested listeners of changes to their properties. For +that purpose, a class that wants to use this policy needs to +implement the ``NotifyPropertyChanged`` interface from the +``Doctrine\Common`` namespace. + +.. code-block:: php + + listeners[] = $listener; + } + + /** Notifies listeners of a change. */ + protected function onPropertyChanged($propName, $oldValue, $newValue) { + if ($this->listeners) { + foreach ($this->listeners as $listener) { + $listener->propertyChanged($this, $propName, $oldValue, $newValue); + } + } + } + } + +Then, in each property setter of concrete, derived domain classes, +you need to invoke onPropertyChanged as follows to notify +listeners: + +.. code-block:: php + + data) { // check: is it actually modified? + $this->onPropertyChanged('data', $this->data, $data); + $this->data = $data; + } + } + } + +The check whether the new value is different from the old one is +not mandatory but recommended. That way you can avoid unnecessary +updates and also have full control over when you consider a +property changed. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst b/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst new file mode 100644 index 00000000000..6a8ef9c91a7 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst @@ -0,0 +1,78 @@ +Implementing Wakeup or Clone +============================ + +.. sectionauthor:: Roman Borschel (roman@code-factory.org) + +As explained in the +`restrictions for entity classes in the manual `_, +it is usually not allowed for an entity to implement ``__wakeup`` +or ``__clone``, because Doctrine makes special use of them. +However, it is quite easy to make use of these methods in a safe +way by guarding the custom wakeup or clone code with an entity +identity check, as demonstrated in the following sections. + +Safely implementing \_\_wakeup +------------------------------ + +To safely implement ``__wakeup``, simply enclose your +implementation code in an identity check as follows: + +.. code-block:: php + + id) { + // ... Your code here as normal ... + } + // otherwise do nothing, do NOT throw an exception! + } + + //... + } + +Safely implementing \_\_clone +----------------------------- + +Safely implementing ``__clone`` is pretty much the same: + +.. code-block:: php + + id) { + // ... Your code here as normal ... + } + // otherwise do nothing, do NOT throw an exception! + } + + //... + } + +Summary +------- + +As you have seen, it is quite easy to safely make use of +``__wakeup`` and ``__clone`` in your entities without adding any +really Doctrine-specific or Doctrine-dependant code. + +These implementations are possible and safe because when Doctrine +invokes these methods, the entities never have an identity (yet). +Furthermore, it is possibly a good idea to check for the identity +in your code anyway, since it's rarely the case that you want to +unserialize or clone an entity with no identity. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst b/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst new file mode 100644 index 00000000000..1c06a34e2c4 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst @@ -0,0 +1,140 @@ +Integrating with CodeIgniter +============================ + +This is recipe for using Doctrine 2 in your +`CodeIgniter `_ framework. + +.. note:: + + This might not work for all CodeIgniter versions and may require + slight adjustments. + + +Here is how to set it up: + +Make a CodeIgniter library that is both a wrapper and a bootstrap +for Doctrine 2. + +Setting up the file structure +----------------------------- + +Here are the steps: + + +- Add a php file to your system/application/libraries folder + called Doctrine.php. This is going to be your wrapper/bootstrap for + the D2 entity manager. +- Put the Doctrine folder (the one that contains Common, DBAL, and + ORM) inside that same libraries folder. +- Your system/application/libraries folder now looks like this: + + system/applications/libraries -Doctrine -Doctrine.php -index.html + +- If you want, open your config/autoload.php file and autoload + your Doctrine library. + + register(); + $entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" )); + $entitiesClassLoader->register(); + $proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies'); + $proxiesClassLoader->register(); + + // Set up caches + $config = new Configuration; + $cache = new ArrayCache; + $config->setMetadataCacheImpl($cache); + $driverImpl = $config->newDefaultAnnotationDriver(array(APPPATH.'models/Entities')); + $config->setMetadataDriverImpl($driverImpl); + $config->setQueryCacheImpl($cache); + + $config->setQueryCacheImpl($cache); + + // Proxy configuration + $config->setProxyDir(APPPATH.'/models/proxies'); + $config->setProxyNamespace('Proxies'); + + // Set up logger + $logger = new EchoSQLLogger; + $config->setSQLLogger($logger); + + $config->setAutoGenerateProxyClasses( TRUE ); + + // Database connection information + $connectionOptions = array( + 'driver' => 'pdo_mysql', + 'user' => $db['default']['username'], + 'password' => $db['default']['password'], + 'host' => $db['default']['hostname'], + 'dbname' => $db['default']['database'] + ); + + // Create EntityManager + $this->em = EntityManager::create($connectionOptions, $config); + } + } + +Please note that this is a development configuration; for a +production system you'll want to use a real caching system like +APC, get rid of EchoSqlLogger, and turn off +autoGenerateProxyClasses. + +For more details, consult the +`Doctrine 2 Configuration documentation `_. + +Now to use it +------------- + +Whenever you need a reference to the entity manager inside one of +your controllers, views, or models you can do this: + +.. code-block:: php + + doctrine->em; + +That's all there is to it. Once you get the reference to your +EntityManager do your Doctrine 2.0 voodoo as normal. + +Note: If you do not choose to autoload the Doctrine library, you +will need to put this line before you get a reference to it: + +.. code-block:: php + + load->library('doctrine'); + +Good luck! + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst b/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst new file mode 100644 index 00000000000..fcc04517b1b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst @@ -0,0 +1,189 @@ +Mysql Enums +=========== + +The type system of Doctrine 2 consists of flyweights, which means there is only +one instance of any given type. Additionally types do not contain state. Both +assumptions make it rather complicated to work with the Enum Type of MySQL that +is used quite a lot by developers. + +When using Enums with a non-tweaked Doctrine 2 application you will get +errors from the Schema-Tool commands due to the unknown database type "enum". +By default Doctrine does not map the MySQL enum type to a Doctrine type. +This is because Enums contain state (their allowed values) and Doctrine +types don't. + +This cookbook entry shows two possible solutions to work with MySQL enums. +But first a word of warning. The MySQL Enum type has considerable downsides: + +- Adding new values requires to rebuild the whole table, which can take hours + depending on the size. +- Enums are ordered in the way the values are specified, not in their "natural" order. +- Enums validation mechanism for allowed values is not necessarily good, + specifying invalid values leads to an empty enum for the default MySQL error + settings. You can easily replicate the "allow only some values" requirement + in your Doctrine entities. + +Solution 1: Mapping to Varchars +------------------------------- + +You can map ENUMs to varchars. You can register MySQL ENUMs to map to Doctrine +varchars. This way Doctrine always resolves ENUMs to Doctrine varchars. It +will even detect this match correctly when using SchemaTool update commands. + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string'); + +In this case you have to ensure that each varchar field that is an enum in the +database only gets passed the allowed values. You can easily enforce this in your +entities: + +.. code-block:: php + + status = $status; + } + } + +If you want to actively create enums through the Doctrine Schema-Tool by using +the **columnDefinition** attribute. + +.. code-block:: php + + values); + + return "ENUM(".implode(", ", $values).") COMMENT '(DC2Type:".$this->name.")'"; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!in_array($value, $this->values)) { + throw new \InvalidArgumentException("Invalid '".$this->name."' value."); + } + return $value; + } + + public function getName() + { + return $this->name; + } + } + +With this base class you can define an enum as easily as: + +.. code-block:: php + + addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array()); + + // Add the ResolveTargetEntityListener + $evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel); + + $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + +Final Thoughts +-------------- + +With the ``ResolveTargetEntityListener``, we are able to decouple our +bundles, keeping them usable by themselves, but still being able to +define relationships between different objects. By using this method, +I've found my bundles end up being easier to maintain independently. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst b/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst new file mode 100644 index 00000000000..fdfd4032f3c --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst @@ -0,0 +1,84 @@ +SQL-Table Prefixes +================== + +This recipe is intended as an example of implementing a +loadClassMetadata listener to provide a Table Prefix option for +your application. The method used below is not a hack, but fully +integrates into the Doctrine system, all SQL generated will include +the appropriate table prefix. + +In most circumstances it is desirable to separate different +applications into individual databases, but in certain cases, it +may be beneficial to have a table prefix for your Entities to +separate them from other vendor products in the same database. + +Implementing the listener +------------------------- + +The listener in this example has been set up with the +DoctrineExtensions namespace. You create this file in your +library/DoctrineExtensions directory, but will need to set up +appropriate autoloaders. + +.. code-block:: php + + prefix = (string) $prefix; + } + + public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) + { + $classMetadata = $eventArgs->getClassMetadata(); + + if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) { + $classMetadata->setTableName($this->prefix . $classMetadata->getTableName()); + } + + foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { + if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) { + $mappedTableName = $mapping['joinTable']['name']; + $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; + } + } + } + + } + +Telling the EntityManager about our listener +-------------------------------------------- + +A listener of this type must be set up before the EntityManager has +been initialised, otherwise an Entity might be created or cached +before the prefix has been set. + +.. note:: + + If you set this listener up, be aware that you will need + to clear your caches and drop then recreate your database schema. + + +.. code-block:: php + + addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $tablePrefix); + + $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst b/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst new file mode 100644 index 00000000000..d9934f577de --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst @@ -0,0 +1,254 @@ +Strategy-Pattern +================ + +This recipe will give you a short introduction on how to design +similar entities without using expensive (i.e. slow) inheritance +but with not more than \* the well-known strategy pattern \* event +listeners + +Scenario / Problem +------------------ + +Given a Content-Management-System, we probably want to add / edit +some so-called "blocks" and "panels". What are they for? + + +- A block might be a registration form, some text content, a table + with information. A good example might also be a small calendar. +- A panel is by definition a block that can itself contain blocks. + A good example for a panel might be a sidebar box: You could easily + add a small calendar into it. + +So, in this scenario, when building your CMS, you will surely add +lots of blocks and panels to your pages and you will find yourself +highly uncomfortable because of the following: + + +- Every existing page needs to know about the panels it contains - + therefore, you'll have an association to your panels. But if you've + got several types of panels - what do you do? Add an association to + every panel-type? This wouldn't be flexible. You might be tempted + to add an AbstractPanelEntity and an AbstractBlockEntity that use + class inheritance. Your page could then only confer to the + AbstractPanelType and Doctrine 2 would do the rest for you, i.e. + load the right entities. But - you'll for sure have lots of panels + and blocks, and even worse, you'd have to edit the discriminator + map *manually* every time you or another developer implements a new + block / entity. This would tear down any effort of modular + programming. + +Therefore, we need something that's far more flexible. + +Solution +-------- + +The solution itself is pretty easy. We will have one base class +that will be loaded via the page and that has specific behaviour - +a Block class might render the front-end and even the backend, for +example. Now, every block that you'll write might look different or +need different data - therefore, we'll offer an API to these +methods but internally, we use a strategy that exactly knows what +to do. + +First of all, we need to make sure that we have an interface that +contains every needed action. Such actions would be rendering the +front-end or the backend, solving dependencies (blocks that are +supposed to be placed in the sidebar could refuse to be placed in +the middle of your page, for example). + +Such an interface could look like this: + + +.. code-block:: php + + blockStrategy. Will not be persisted by Doctrine 2. + * + * @var BlockStrategyInterface + */ + protected $strategyInstance; + + /** + * Returns the strategy that is used for this blockitem. + * + * The strategy itself defines how this block can be rendered etc. + * + * @return string + */ + public function getStrategyClassName() { + return $this->strategyClassName; + } + + /** + * Returns the instantiated strategy + * + * @return BlockStrategyInterface + */ + public function getStrategyInstance() { + return $this->strategyInstance; + } + + /** + * Sets the strategy this block / panel should work as. Make sure that you've used + * this method before persisting the block! + * + * @param BlockStrategyInterface $strategy + */ + public function setStrategy(BlockStrategyInterface $strategy) { + $this->strategyInstance = $strategy; + $this->strategyClassName = get_class($strategy); + $strategy->setBlockEntity($this); + } + +Now, the important point is that $strategyClassName is a Doctrine 2 +field, i.e. Doctrine will persist this value. This is only the +class name of your strategy and not an instance! + +Finishing your strategy pattern, we hook into the Doctrine postLoad +event and check whether a block has been loaded. If so, you will +initialize it - i.e. get the strategies classname, create an +instance of it and set it via setStrategyBlock(). + +This might look like this: + +.. code-block:: php + + view = $view; + } + + public function getSubscribedEvents() { + return array(ORM\Events::postLoad); + } + + public function postLoad(ORM\Event\LifecycleEventArgs $args) { + $blockItem = $args->getEntity(); + + // Both blocks and panels are instances of Block\AbstractBlock + if ($blockItem instanceof Block\AbstractBlock) { + $strategy = $blockItem->getStrategyClassName(); + $strategyInstance = new $strategy(); + if (null !== $blockItem->getConfig()) { + $strategyInstance->setConfig($blockItem->getConfig()); + } + $strategyInstance->setView($this->view); + $blockItem->setStrategy($strategyInstance); + } + } + } + +In this example, even some variables are set - like a view object +or a specific configuration object. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst b/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst new file mode 100644 index 00000000000..fb0254417b0 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst @@ -0,0 +1,137 @@ +Validation of Entities +====================== + +.. sectionauthor:: Benjamin Eberlei + +Doctrine 2 does not ship with any internal validators, the reason +being that we think all the frameworks out there already ship with +quite decent ones that can be integrated into your Domain easily. +What we offer are hooks to execute any kind of validation. + +.. note:: + + You don't need to validate your entities in the lifecycle + events. Its only one of many options. Of course you can also + perform validations in value setters or any other method of your + entities that are used in your code. + + +Entities can register lifecycle event methods with Doctrine that +are called on different occasions. For validation we would need to +hook into the events called before persisting and updating. Even +though we don't support validation out of the box, the +implementation is even simpler than in Doctrine 1 and you will get +the additional benefit of being able to re-use your validation in +any other part of your domain. + +Say we have an ``Order`` with several ``OrderLine`` instances. We +never want to allow any customer to order for a larger sum than he +is allowed to: + +.. code-block:: php + + customer->getOrderLimit(); + + $amount = 0; + foreach ($this->orderLines as $line) { + $amount += $line->getAmount(); + } + + if ($amount > $orderLimit) { + throw new CustomerOrderLimitExceededException(); + } + } + } + +Now this is some pretty important piece of business logic in your +code, enforcing it at any time is important so that customers with +a unknown reputation don't owe your business too much money. + +We can enforce this constraint in any of the metadata drivers. +First Annotations: + +.. code-block:: php + + + + + + + + + + +YAML needs some little change yet, to allow multiple lifecycle +events for one method, this will happen before Beta 1 though. + +Now validation is performed whenever you call +``EntityManager#persist($order)`` or when you call +``EntityManager#flush()`` and an order is about to be updated. Any +Exception that happens in the lifecycle callbacks will be cached by +the EntityManager and the current transaction is rolled back. + +Of course you can do any type of primitive checks, not null, +email-validation, string size, integer and date ranges in your +validation callbacks. + +.. code-block:: php + + plannedShipDate instanceof DateTime)) { + throw new ValidateException(); + } + + if ($this->plannedShipDate->format('U') < time()) { + throw new ValidateException(); + } + + if ($this->customer == null) { + throw new OrderRequiresCustomerException(); + } + } + } + +What is nice about lifecycle events is, you can also re-use the +methods at other places in your domain, for example in combination +with your form library. Additionally there is no limitation in the +number of methods you register on one particular event, i.e. you +can register multiple methods for validation in "PrePersist" or +"PreUpdate" or mix and share them in any combinations between those +two events. + +There is no limit to what you can and can't validate in +"PrePersist" and "PreUpdate" as long as you don't create new entity +instances. This was already discussed in the previous blog post on +the Versionable extension, which requires another type of event +called "onFlush". + +Further readings: :doc:`Lifecycle Events <../reference/events>` diff --git a/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst b/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst new file mode 100644 index 00000000000..fc548dac0fc --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst @@ -0,0 +1,168 @@ +Working with DateTime Instances +=============================== + +There are many nitty gritty details when working with PHPs DateTime instances. You have know their inner +workings pretty well not to make mistakes with date handling. This cookbook entry holds several +interesting pieces of information on how to work with PHP DateTime instances in Doctrine 2. + +DateTime changes are detected by Reference +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When calling ``EntityManager#flush()`` Doctrine computes the changesets of all the currently managed entities +and saves the differences to the database. In case of object properties (@Column(type="datetime") or @Column(type="object")) +these comparisons are always made **BY REFERENCE**. That means the following change will **NOT** be saved into the database: + +.. code-block:: php + + updated->modify("now"); + } + } + +The way to go would be: + +.. code-block:: php + + updated = new \DateTime("now"); + } + } + +Default Timezone Gotcha +~~~~~~~~~~~~~~~~~~~~~~~ + +By default Doctrine assumes that you are working with a default timezone. Each DateTime instance that +is created by Doctrine will be assigned the timezone that is currently the default, either through +the ``date.timezone`` ini setting or by calling ``date_default_timezone_set()``. + +This is very important to handle correctly if your application runs on different serves or is moved from one to another server +(with different timezone settings). You have to make sure that the timezone is the correct one +on all this systems. + +Handling different Timezones with the DateTime Type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you first come across the requirement to save different you are still optimistic to manage this mess, +however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2) +that supports timezones correctly. Correctly here means that you can cover all the use-cases that +can come up with timezones. If you don't believe me you should read up on `Storing DateTime +in Databases `_. + +The problem is simple. Not a single database vendor saves the timezone, only the differences to UTC. +However with frequent daylight saving and political timezone changes you can have a UTC offset that moves +in different offset directions depending on the real location. + +The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine 2. However there is a workaround +that even allows correct date-time handling with timezones: + +1. Always convert any DateTime instance to UTC. +2. Only set Timezones for displaying purposes +3. Save the Timezone in the Entity for persistence. + +Say we have an application for an international postal company and employees insert events regarding postal-package +around the world, in their current timezones. To determine the exact time an event occurred means to save both +the UTC time at the time of the booking and the timezone the event happened in. + +.. code-block:: php + + format($platform->getDateTimeFormatString(), + (self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC')) + ); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = \DateTime::createFromFormat( + $platform->getDateTimeFormatString(), + $value, + (self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC')) + ); + if (!$val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } + } + +This database type makes sure that every DateTime instance is always saved in UTC, relative +to the current timezone that the passed DateTime instance has. To be able to transform these values +back into their real timezone you have to save the timezone in a separate field of the entity +requiring timezoned datetimes: + +.. code-block:: php + + localized = true; + $this->created = $createDate; + $this->timezone = $createDate->getTimeZone()->getName(); + } + + public function getCreated() + { + if (!$this->localized) { + $this->created->setTimeZone(new \DateTimeZone($this->timezone)); + } + return $this->created; + } + } + +This snippet makes use of the previously discussed "changeset by reference only" property of +objects. That means a new DateTime will only be used during updating if the reference +changes between retrieval and flush operation. This means we can easily go and modify +the instance by setting the previous local timezone. diff --git a/vendor/doctrine/orm/docs/en/index.rst b/vendor/doctrine/orm/docs/en/index.rst new file mode 100644 index 00000000000..8e6afc82592 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/index.rst @@ -0,0 +1,131 @@ +Welcome to Doctrine 2 ORM's documentation! +========================================== + +The Doctrine documentation is comprised of tutorials, a reference section and +cookbook articles that explain different parts of the Object Relational mapper. + +Doctrine DBAL and Doctrine Common both have their own documentation. + +Getting Help +------------ + +If this documentation is not helping to answer questions you have about +Doctrine ORM don't panic. You can get help from different sources: + +- There is a :doc:`FAQ ` with answers to frequent questions. +- The `Doctrine Mailing List `_ +- Internet Relay Chat (IRC) in #doctrine on Freenode +- Report a bug on `JIRA `_. +- On `Twitter `_ with ``#doctrine2`` +- On `StackOverflow `_ + +If you need more structure over the different topics you can browse the :doc:`table +of contents `. + +Getting Started +--------------- + +* **Tutorial**: + :doc:`Getting Started with Doctrine ` + +* **Setup**: + :doc:`Installation & Configuration ` + +Mapping Objects onto a Database +------------------------------- + +* **Mapping**: + :doc:`Objects ` | + :doc:`Associations ` | + :doc:`Inheritance ` + +* **Drivers**: + :doc:`Docblock Annotations ` | + :doc:`XML ` | + :doc:`YAML ` | + :doc:`PHP ` + +Working with Objects +-------------------- + +* **Basic Reference**: + :doc:`Entities ` | + :doc:`Associations ` | + :doc:`Events ` + +* **Query Reference**: + :doc:`DQL ` | + :doc:`QueryBuilder ` | + :doc:`Native SQL ` + +* **Internals**: + :doc:`Internals explained ` | + :doc:`Associations ` + +Advanced Topics +--------------- + +* :doc:`Architecture ` +* :doc:`Advanced Configuration ` +* :doc:`Limitations and known issues ` +* :doc:`Commandline Tools ` +* :doc:`Transactions and Concurrency ` +* :doc:`Filters ` +* :doc:`NamingStrategy ` +* :doc:`Improving Performance ` +* :doc:`Caching ` +* :doc:`Partial Objects ` +* :doc:`Change Tracking Policies ` +* :doc:`Best Practices ` +* :doc:`Metadata Drivers ` +* :doc:`Batch Processing ` +* :doc:`Second Level Cache ` + +Tutorials +--------- + +* :doc:`Indexed associations ` +* :doc:`Extra Lazy Associations ` +* :doc:`Composite Primary Keys ` +* :doc:`Ordered associations ` +* :doc:`Pagination ` +* :doc:`Override Field/Association Mappings In Subclasses ` +* :doc:`Embeddables ` + +Changelogs +---------- + +* :doc:`Migration to 2.5 ` + +Cookbook +-------- + +* **Patterns**: + :doc:`Aggregate Fields ` | + :doc:`Decorator Pattern ` | + :doc:`Strategy Pattern ` + +* **DQL Extension Points**: + :doc:`DQL Custom Walkers ` | + :doc:`DQL User-Defined-Functions ` + +* **Implementation**: + :doc:`Array Access ` | + :doc:`Notify ChangeTracking Example ` | + :doc:`Using Wakeup Or Clone ` | + :doc:`Working with DateTime ` | + :doc:`Validation ` | + :doc:`Entities in the Session ` | + :doc:`Keeping your Modules independent ` + +* **Integration into Frameworks/Libraries** + :doc:`CodeIgniter ` + +* **Hidden Gems** + :doc:`Prefixing Table Name ` + +* **Custom Datatypes** + :doc:`MySQL Enums ` + :doc:`Advanced Field Value Conversion ` + +.. include:: toc.rst diff --git a/vendor/doctrine/orm/docs/en/make.bat b/vendor/doctrine/orm/docs/en/make.bat new file mode 100644 index 00000000000..53c40c9129a --- /dev/null +++ b/vendor/doctrine/orm/docs/en/make.bat @@ -0,0 +1,113 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +set SPHINXBUILD=sphinx-build +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst b/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst new file mode 100644 index 00000000000..9d200f87005 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst @@ -0,0 +1,468 @@ +Advanced Configuration +====================== + +The configuration of the EntityManager requires a +``Doctrine\ORM\Configuration`` instance as well as some database +connection parameters. This example shows all the potential +steps of configuration. + +.. code-block:: php + + setMetadataCacheImpl($cache); + $driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities'); + $config->setMetadataDriverImpl($driverImpl); + $config->setQueryCacheImpl($cache); + $config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies'); + $config->setProxyNamespace('MyProject\Proxies'); + + if ($applicationMode == "development") { + $config->setAutoGenerateProxyClasses(true); + } else { + $config->setAutoGenerateProxyClasses(false); + } + + $connectionOptions = array( + 'driver' => 'pdo_sqlite', + 'path' => 'database.sqlite' + ); + + $em = EntityManager::create($connectionOptions, $config); + +.. note:: + + Do not use Doctrine without a metadata and query cache! + Doctrine is optimized for working with caches. The main + parts in Doctrine that are optimized for caching are the metadata + mapping information with the metadata cache and the DQL to SQL + conversions with the query cache. These 2 caches require only an + absolute minimum of memory yet they heavily improve the runtime + performance of Doctrine. The recommended cache driver to use with + Doctrine is `APC `_. APC provides you with + an opcode-cache (which is highly recommended anyway) and a very + fast in-memory cache storage that you can use for the metadata and + query caches as seen in the previous code snippet. + +Configuration Options +--------------------- + +The following sections describe all the configuration options +available on a ``Doctrine\ORM\Configuration`` instance. + +Proxy Directory (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setProxyDir($dir); + $config->getProxyDir(); + +Gets or sets the directory where Doctrine generates any proxy +classes. For a detailed explanation on proxy classes and how they +are used in Doctrine, refer to the "Proxy Objects" section further +down. + +Proxy Namespace (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setProxyNamespace($namespace); + $config->getProxyNamespace(); + +Gets or sets the namespace to use for generated proxy classes. For +a detailed explanation on proxy classes and how they are used in +Doctrine, refer to the "Proxy Objects" section further down. + +Metadata Driver (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setMetadataDriverImpl($driver); + $config->getMetadataDriverImpl(); + +Gets or sets the metadata driver implementation that is used by +Doctrine to acquire the object-relational metadata for your +classes. + +There are currently 4 available implementations: + + +- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` +- ``Doctrine\ORM\Mapping\Driver\XmlDriver`` +- ``Doctrine\ORM\Mapping\Driver\YamlDriver`` +- ``Doctrine\ORM\Mapping\Driver\DriverChain`` + +Throughout the most part of this manual the AnnotationDriver is +used in the examples. For information on the usage of the XmlDriver +or YamlDriver please refer to the dedicated chapters +``XML Mapping`` and ``YAML Mapping``. + +The annotation driver can be configured with a factory method on +the ``Doctrine\ORM\Configuration``: + +.. code-block:: php + + newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities'); + $config->setMetadataDriverImpl($driverImpl); + +The path information to the entities is required for the annotation +driver, because otherwise mass-operations on all entities through +the console could not work correctly. All of metadata drivers +accept either a single directory as a string or an array of +directories. With this feature a single driver can support multiple +directories of Entities. + +Metadata Cache (***RECOMMENDED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setMetadataCacheImpl($cache); + $config->getMetadataCacheImpl(); + +Gets or sets the cache implementation to use for caching metadata +information, that is, all the information you supply via +annotations, xml or yaml, so that they do not need to be parsed and +loaded from scratch on every single request which is a waste of +resources. The cache implementation must implement the +``Doctrine\Common\Cache\Cache`` interface. + +Usage of a metadata cache is highly recommended. + +The recommended implementations for production are: + + +- ``Doctrine\Common\Cache\ApcCache`` +- ``Doctrine\Common\Cache\MemcacheCache`` +- ``Doctrine\Common\Cache\XcacheCache`` +- ``Doctrine\Common\Cache\RedisCache`` + +For development you should use the +``Doctrine\Common\Cache\ArrayCache`` which only caches data on a +per-request basis. + +Query Cache (***RECOMMENDED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setQueryCacheImpl($cache); + $config->getQueryCacheImpl(); + +Gets or sets the cache implementation to use for caching DQL +queries, that is, the result of a DQL parsing process that includes +the final SQL as well as meta information about how to process the +SQL result set of a query. Note that the query cache does not +affect query results. You do not get stale data. This is a pure +optimization cache without any negative side-effects (except some +minimal memory usage in your cache). + +Usage of a query cache is highly recommended. + +The recommended implementations for production are: + + +- ``Doctrine\Common\Cache\ApcCache`` +- ``Doctrine\Common\Cache\MemcacheCache`` +- ``Doctrine\Common\Cache\XcacheCache`` +- ``Doctrine\Common\Cache\RedisCache`` + +For development you should use the +``Doctrine\Common\Cache\ArrayCache`` which only caches data on a +per-request basis. + +SQL Logger (***Optional***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setSQLLogger($logger); + $config->getSQLLogger(); + +Gets or sets the logger to use for logging all SQL statements +executed by Doctrine. The logger class must implement the +``Doctrine\DBAL\Logging\SQLLogger`` interface. A simple default +implementation that logs to the standard output using ``echo`` and +``var_dump`` can be found at +``Doctrine\DBAL\Logging\EchoSQLLogger``. + +Auto-generating Proxy Classes (***OPTIONAL***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Proxy classes can either be generated manually through the Doctrine +Console or automatically at runtime by Doctrine. The configuration +option that controls this behavior is: + +.. code-block:: php + + setAutoGenerateProxyClasses($mode); + +Possible values for ``$mode`` are: + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER`` + +Never autogenerate a proxy. You will need to generate the proxies +manually, for this use the Doctrine Console like so: + +.. code-block:: php + + $ ./doctrine orm:generate-proxies + +When you do this in a development environment, +be aware that you may get class/file not found errors if certain proxies +are not yet generated. You may also get failing lazy-loads if new +methods were added to the entity class that are not yet in the proxy class. +In such a case, simply use the Doctrine Console to (re)generate the +proxy classes. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS`` + +Always generates a new proxy in every request and writes it to disk. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS`` + +Generate the proxy class when the proxy file does not exist. +This strategy causes a file exists call whenever any proxy is +used the first time in a request. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL`` + +Generate the proxy classes and evaluate them on the fly via eval(), +avoiding writing the proxies to disk. +This strategy is only sane for development. + +In a production environment, it is highly recommended to use +AUTOGENERATE_NEVER to allow for optimal performances. The other +options are interesting in development environment. + +Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean +value. This is still possible, ``FALSE`` being equivalent to +AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS. + +Development vs Production Configuration +--------------------------------------- + +You should code your Doctrine2 bootstrapping with two different +runtime models in mind. There are some serious benefits of using +APC or Memcache in production. In development however this will +frequently give you fatal errors, when you change your entities and +the cache still keeps the outdated metadata. That is why we +recommend the ``ArrayCache`` for development. + +Furthermore you should have the Auto-generating Proxy Classes +option to true in development and to false in production. If this +option is set to ``TRUE`` it can seriously hurt your script +performance if several proxy classes are re-generated during script +execution. Filesystem calls of that magnitude can even slower than +all the database queries Doctrine issues. Additionally writing a +proxy sets an exclusive file lock which can cause serious +performance bottlenecks in systems with regular concurrent +requests. + +Connection Options +------------------ + +The ``$connectionOptions`` passed as the first argument to +``EntityManager::create()`` has to be either an array or an +instance of ``Doctrine\DBAL\Connection``. If an array is passed it +is directly passed along to the DBAL Factory +``Doctrine\DBAL\DriverManager::getConnection()``. The DBAL +configuration is explained in the +`DBAL section <./../../../../../projects/doctrine-dbal/en/latest/reference/configuration.html>`_. + +Proxy Objects +------------- + +A proxy object is an object that is put in place or used instead of +the "real" object. A proxy object can add behavior to the object +being proxied without that object being aware of it. In Doctrine 2, +proxy objects are used to realize several features but mainly for +transparent lazy-loading. + +Proxy objects with their lazy-loading facilities help to keep the +subset of objects that are already in memory connected to the rest +of the objects. This is an essential property as without it there +would always be fragile partial objects at the outer edges of your +object graph. + +Doctrine 2 implements a variant of the proxy pattern where it +generates classes that extend your entity classes and adds +lazy-loading capabilities to them. Doctrine can then give you an +instance of such a proxy class whenever you request an object of +the class being proxied. This happens in two situations: + +Reference Proxies +~~~~~~~~~~~~~~~~~ + +The method ``EntityManager#getReference($entityName, $identifier)`` +lets you obtain a reference to an entity for which the identifier +is known, without loading that entity from the database. This is +useful, for example, as a performance enhancement, when you want to +establish an association to an entity for which you have the +identifier. You could simply do this: + +.. code-block:: php + + getReference('MyProject\Model\Item', $itemId); + $cart->addItem($item); + +Here, we added an Item to a Cart without loading the Item from the +database. If you invoke any method on the Item instance, it would +fully initialize its state transparently from the database. Here +$item is actually an instance of the proxy class that was generated +for the Item class but your code does not need to care. In fact it +**should not care**. Proxy objects should be transparent to your +code. + +Association proxies +~~~~~~~~~~~~~~~~~~~ + +The second most important situation where Doctrine uses proxy +objects is when querying for objects. Whenever you query for an +object that has a single-valued association to another object that +is configured LAZY, without joining that association in the same +query, Doctrine puts proxy objects in place where normally the +associated object would be. Just like other proxies it will +transparently initialize itself on first access. + +.. note:: + + Joining an association in a DQL or native query + essentially means eager loading of that association in that query. + This will override the 'fetch' option specified in the mapping for + that association, but only for that query. + + +Generating Proxy classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +In a production environment, it is highly recommended to use +``AUTOGENERATE_NEVER`` to allow for optimal performances. +However you will be required to generate the proxies manually +using the Doctrine Console: + +.. code-block:: php + + $ ./doctrine orm:generate-proxies + +The other options are interesting in development environment: + +- ``AUTOGENERATE_ALWAYS`` will require you to create and configure + a proxy directory. Proxies will be generated and written to file + on each request, so any modification to your code will be acknowledged. + +- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing + proxy file. If your code changes, you will need to regenerate the + proxies manually. + +- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request, + but without writing them to disk. + +Autoloading Proxies +------------------- + +When you deserialize proxy objects from the session or any other storage +it is necessary to have an autoloading mechanism in place for these classes. +For implementation reasons Proxy class names are not PSR-0 compliant. This +means that you have to register a special autoloader for these classes: + +.. code-block:: php + + addDriver($xmlDriver, 'Doctrine\Tests\Models\Company'); + $chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping'); + +Based on the namespace of the entity the loading of entities is +delegated to the appropriate driver. The chain semantics come from +the fact that the driver loops through all namespaces and matches +the entity class name against the namespace using a +``strpos() === 0`` call. This means you need to order the drivers +correctly if sub-namespaces use different metadata driver +implementations. + + +Default Repository (***OPTIONAL***) +----------------------------------- + +Specifies the FQCN of a subclass of the EntityRepository. +That will be available for all entities without a custom repository class. + +.. code-block:: php + + setDefaultRepositoryClassName($fqcn); + $config->getDefaultRepositoryClassName(); + +The default value is ``Doctrine\ORM\EntityRepository``. +Any repository class must be a subclass of EntityRepository otherwise you got an ORMException + +Setting up the Console +---------------------- + +Doctrine uses the Symfony Console component for generating the command +line interface. You can take a look at the ``vendor/bin/doctrine.php`` +script and the ``Doctrine\ORM\Tools\Console\ConsoleRunner`` command +for inspiration how to setup the cli. + +In general the required code looks like this: + +.. code-block:: php + + setCatchExceptions(true); + $cli->setHelperSet($helperSet); + Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($cli); + $cli->run(); + diff --git a/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst b/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst new file mode 100644 index 00000000000..d6d700b834f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst @@ -0,0 +1,1231 @@ +Annotations Reference +===================== + +You've probably used docblock annotations in some form already, +most likely to provide documentation metadata for a tool like +``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a +tool to embed metadata inside the documentation section which can +then be processed by some tool. Doctrine 2 generalizes the concept +of docblock annotations so that they can be used for any kind of +metadata and so that it is easy to define new docblock annotations. +In order to allow more involved annotation values and to reduce the +chances of clashes with other docblock annotations, the Doctrine 2 +docblock annotations feature an alternative syntax that is heavily +inspired by the Annotation syntax introduced in Java 5. + +The implementation of these enhanced docblock annotations is +located in the ``Doctrine\Common\Annotations`` namespace and +therefore part of the Common package. Doctrine 2 docblock +annotations support namespaces and nested annotations among other +things. The Doctrine 2 ORM defines its own set of docblock +annotations for supplying object-relational mapping metadata. + +.. note:: + + If you're not comfortable with the concept of docblock + annotations, don't worry, as mentioned earlier Doctrine 2 provides + XML and YAML alternatives and you could easily implement your own + favourite mechanism for defining ORM metadata. + +In this chapter a reference of every Doctrine 2 Annotation is given +with short explanations on their context and usage. + +Index +----- + +- :ref:`@Column ` +- :ref:`@ColumnResult ` +- :ref:`@Cache ` +- :ref:`@ChangeTrackingPolicy ` +- :ref:`@DiscriminatorColumn ` +- :ref:`@DiscriminatorMap ` +- :ref:`@Entity ` +- :ref:`@EntityResult ` +- :ref:`@FieldResult ` +- :ref:`@GeneratedValue ` +- :ref:`@HasLifecycleCallbacks ` +- :ref:`@Index ` +- :ref:`@Id ` +- :ref:`@InheritanceType ` +- :ref:`@JoinColumn ` +- :ref:`@JoinColumns ` +- :ref:`@JoinTable ` +- :ref:`@ManyToOne ` +- :ref:`@ManyToMany ` +- :ref:`@MappedSuperclass ` +- :ref:`@NamedNativeQuery ` +- :ref:`@OneToOne ` +- :ref:`@OneToMany ` +- :ref:`@OrderBy ` +- :ref:`@PostLoad ` +- :ref:`@PostPersist ` +- :ref:`@PostRemove ` +- :ref:`@PostUpdate ` +- :ref:`@PrePersist ` +- :ref:`@PreRemove ` +- :ref:`@PreUpdate ` +- :ref:`@SequenceGenerator ` +- :ref:`@SqlResultSetMapping ` +- :ref:`@Table ` +- :ref:`@UniqueConstraint ` +- :ref:`@Version ` + +Reference +--------- + +.. _annref_column: + +@Column +~~~~~~~ + +Marks an annotated instance variable as "persistent". It has to be +inside the instance variables PHP DocBlock comment. Any value hold +inside this variable will be saved to and loaded from the database +as part of the lifecycle of the instance variables entity-class. + +Required attributes: + +- **type**: Name of the Doctrine Type which is converted between PHP + and Database representation. + +Optional attributes: + +- **name**: By default the property name is used for the database + column name also, however the 'name' attribute allows you to + determine the column name. + +- **length**: Used by the "string" type to determine its maximum + length in the database. Doctrine does not validate the length of a + string values for you. + +- **precision**: The precision for a decimal (exact numeric) column + (applies only for decimal column), which is the maximum number of + digits that are stored for the values. + +- **scale**: The scale for a decimal (exact numeric) column (applies + only for decimal column), which represents the number of digits + to the right of the decimal point and must not be greater than + *precision*. + +- **unique**: Boolean value to determine if the value of the column + should be unique across all rows of the underlying entities table. + +- **nullable**: Determines if NULL values allowed for this column. + +- **options**: Array of additional options: + + - ``default``: The default value to set for the column if no value + is supplied. + + - ``unsigned``: Boolean value to determine if the column should + be capable of representing only non-negative integers + (applies only for integer column and might not be supported by + all vendors). + + - ``fixed``: Boolean value to determine if the specified length of + a string column should be fixed or varying (applies only for + string/binary column and might not be supported by all vendors). + + - ``comment``: The comment of the column in the schema (might not + be supported by all vendors). + + - ``collation``: The collation of the column (only supported by Drizzle, Mysql, PostgreSQL>=9.1, Sqlite and SQLServer). + +- **columnDefinition**: DDL SQL snippet that starts after the column + name and specifies the complete (non-portable!) column definition. + This attribute allows to make use of advanced RMDBS features. + However you should make careful use of this feature and the + consequences. SchemaTool will not detect changes on the column correctly + anymore if you use "columnDefinition". + + Additionally you should remember that the "type" + attribute still handles the conversion between PHP and Database + values. If you use this attribute on a column that is used for + joins between tables you should also take a look at + :ref:`@JoinColumn `. + +.. note:: + + For more detailed information on each attribute, please refer to + the DBAL ``Schema-Representation`` documentation. + +Examples: + +.. code-block:: php + + ` +can be found in the configuration section. + +Example: + +.. code-block:: php + + = 2.1) Specifies that this entity is marked as read only and not + considered for change-tracking. Entities of this type can be persisted + and removed though. + +Example: + +.. code-block:: php + + `. This +annotation is optional and only has meaning when used in +conjunction with @Id. + +If this annotation is not specified with @Id the NONE strategy is +used as default. + +Required attributes: + + +- **strategy**: Set the name of the identifier generation strategy. + Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE. + +Example: + +.. code-block:: php + + ` annotation on +the entity-class level. It provides a hint to the SchemaTool to +generate a database index on the specified table columns. It only +has meaning in the SchemaTool schema generation context. + +Required attributes: + + +- **name**: Name of the Index +- **columns**: Array of columns. + +Optional attributes: + +- **options**: Array of platform specific options: + + - ``where``: SQL WHERE condition to be used for partial indexes. It will + only have effect on supported platforms. + +Basic example: + +.. code-block:: php + + ` and +:ref:`@DiscriminatorColumn ` annotations. + +Examples: + +.. code-block:: php + + `, :ref:`@OneToOne ` fields +and in the Context of :ref:`@JoinTable ` nested inside +a @ManyToMany. This annotation is not required. If it is not +specified the attributes *name* and *referencedColumnName* are +inferred from the table and primary key names. + +Required attributes: + + +- **name**: Column name that holds the foreign key identifier for + this relation. In the context of @JoinTable it specifies the column + name in the join table. +- **referencedColumnName**: Name of the primary key identifier that + is used for joining of this relation. + +Optional attributes: + + +- **unique**: Determines whether this relation is exclusive between the + affected entities and should be enforced as such on the database + constraint level. Defaults to false. +- **nullable**: Determine whether the related entity is required, or if + null is an allowed state for the relation. Defaults to true. +- **onDelete**: Cascade Action (Database-level) +- **columnDefinition**: DDL SQL snippet that starts after the column + name and specifies the complete (non-portable!) column definition. + This attribute enables the use of advanced RMDBS features. Using + this attribute on @JoinColumn is necessary if you need slightly + different column definitions for joining columns, for example + regarding NULL/NOT NULL defaults. However by default a + "columnDefinition" attribute on :ref:`@Column ` also sets + the related @JoinColumn's columnDefinition. This is necessary to + make foreign keys work. + +Example: + +.. code-block:: php + + ` or :ref:`@OneToOne ` +relation with an entity that has multiple identifiers. + +.. _annref_jointable: + +@JoinTable +~~~~~~~~~~~~~~ + +Using :ref:`@OneToMany ` or +:ref:`@ManyToMany ` on the owning side of the relation +requires to specify the @JoinTable annotation which describes the +details of the database join table. If you do not specify +@JoinTable on these relations reasonable mapping defaults apply +using the affected table and the column names. + +Optional attributes: + + +- **name**: Database name of the join-table +- **joinColumns**: An array of @JoinColumn annotations describing the + join-relation between the owning entities table and the join table. +- **inverseJoinColumns**: An array of @JoinColumn annotations + describing the join-relation between the inverse entities table and + the join table. + +Example: + +.. code-block:: php + + ` is an +additional, optional annotation that has reasonable default +configuration values using the table and names of the two related +entities. + +Required attributes: + + +- **targetEntity**: FQCN of the referenced target entity. Can be the + unqualified class name if both classes are in the same namespace. + *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- **mappedBy**: This option specifies the property name on the + targetEntity that is the owning side of this relation. It is a + required attribute for the inverse side of a relationship. +- **inversedBy**: The inversedBy attribute designates the field in the + entity that is the inverse side of the relationship. +- **cascade**: Cascade Option +- **fetch**: One of LAZY, EXTRA_LAZY or EAGER +- **indexBy**: Index the collection by a field on the target entity. + +.. note:: + + For ManyToMany bidirectional relationships either side may + be the owning side (the side that defines the @JoinTable and/or + does not make use of the mappedBy attribute, thus using a default + join table). + +Example: + +.. code-block:: php + + `. + +Optional attributes: + + +- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository. + That will be inherited for all subclasses of that Mapped Superclass. + +Example: + +.. code-block:: php + + ` with one additional option which can +be specified. The configuration defaults for +:ref:`@JoinColumn ` using the target entity table and +primary key column names apply here too. + +Required attributes: + + +- **targetEntity**: FQCN of the referenced target entity. Can be the + unqualified class name if both classes are in the same namespace. + *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- **cascade**: Cascade Option +- **fetch**: One of LAZY or EAGER +- **orphanRemoval**: Boolean that specifies if orphans, inverse + OneToOne entities that are not connected to any owning instance, + should be removed by Doctrine. Defaults to false. +- **inversedBy**: The inversedBy attribute designates the field in the + entity that is the inverse side of the relationship. + +Example: + +.. code-block:: php + + ` or :ref:`@OneToMany ` +annotation to specify by which criteria the collection should be +retrieved from the database by using an ORDER BY clause. + +This annotation requires a single non-attributed value with an DQL +snippet: + +Example: + +.. code-block:: php + + = 2.5) Name of the schema the table lies in. + +Example: + +.. code-block:: php + + ` annotation on +the entity-class level. It allows to hint the SchemaTool to +generate a database unique constraint on the specified table +columns. It only has meaning in the SchemaTool schema generation +context. + +Required attributes: + + +- **name**: Name of the Index +- **columns**: Array of columns. + +Optional attributes: + +- **options**: Array of platform specific options: + + - ``where``: SQL WHERE condition to be used for partial indexes. It will + only have effect on supported platforms. + +Basic example: + +.. code-block:: php + + ` annotations that have the type integer or +datetime. Combining @Version with :ref:`@Id ` is not supported. + +Example: + +.. code-block:: php + + `. +- An entity class must not implement ``__wakeup`` or + :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`. + Also consider implementing + `Serializable `_ + instead. +- Any two entity classes in a class hierarchy that inherit + directly or indirectly from one another must not have a mapped + property with the same name. That is, if B inherits from A then B + must not have a mapped field with the same name as an already + mapped field that is inherited from A. +- An entity cannot make use of func_get_args() to implement variable parameters. + Generated proxies do not support this for performance reasons and your code might + actually fail to work when violating this restriction. + +Entities support inheritance, polymorphic associations, and +polymorphic queries. Both abstract and concrete classes can be +entities. Entities may extend non-entity classes as well as entity +classes, and non-entity classes may extend entity classes. + +.. note:: + + The constructor of an entity is only ever invoked when + *you* construct a new instance with the *new* keyword. Doctrine + never calls entity constructors, thus you are free to use them as + you wish and even have it require arguments of any type. + + +Entity states +~~~~~~~~~~~~~ + +An entity instance can be characterized as being NEW, MANAGED, +DETACHED or REMOVED. + + +- A NEW entity instance has no persistent identity, and is not yet + associated with an EntityManager and a UnitOfWork (i.e. those just + created with the "new" operator). +- A MANAGED entity instance is an instance with a persistent + identity that is associated with an EntityManager and whose + persistence is thus managed. +- A DETACHED entity instance is an instance with a persistent + identity that is not (or no longer) associated with an + EntityManager and a UnitOfWork. +- A REMOVED entity instance is an instance with a persistent + identity, associated with an EntityManager, that will be removed + from the database upon transaction commit. + +.. _architecture_persistent_fields: + +Persistent fields +~~~~~~~~~~~~~~~~~ + +The persistent state of an entity is represented by instance +variables. An instance variable must be directly accessed only from +within the methods of the entity by the entity instance itself. +Instance variables must not be accessed by clients of the entity. +The state of the entity is available to clients only through the +entity’s methods, i.e. accessor methods (getter/setter methods) or +other business methods. + +Collection-valued persistent fields and properties must be defined +in terms of the ``Doctrine\Common\Collections\Collection`` +interface. The collection implementation type may be used by the +application to initialize fields or properties before the entity is +made persistent. Once the entity becomes managed (or detached), +subsequent access must be through the interface type. + +Serializing entities +~~~~~~~~~~~~~~~~~~~~ + +Serializing entities can be problematic and is not really +recommended, at least not as long as an entity instance still holds +references to proxy objects or is still managed by an +EntityManager. If you intend to serialize (and unserialize) entity +instances that still hold references to proxy objects you may run +into problems with private properties because of technical +limitations. Proxy objects implement ``__sleep`` and it is not +possible for ``__sleep`` to return names of private properties in +parent classes. On the other hand it is not a solution for proxy +objects to implement ``Serializable`` because Serializable does not +work well with any potential cyclic object references (at least we +did not find a way yet, if you did, please contact us). + +The EntityManager +~~~~~~~~~~~~~~~~~ + +The ``EntityManager`` class is a central access point to the ORM +functionality provided by Doctrine 2. The ``EntityManager`` API is +used to manage the persistence of your objects and to query for +persistent objects. + +Transactional write-behind +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An ``EntityManager`` and the underlying ``UnitOfWork`` employ a +strategy called "transactional write-behind" that delays the +execution of SQL statements in order to execute them in the most +efficient way and to execute them at the end of a transaction so +that all write locks are quickly released. You should see Doctrine +as a tool to synchronize your in-memory objects with the database +in well defined units of work. Work with your objects and modify +them as usual and when you're done call ``EntityManager#flush()`` +to make your changes persistent. + +The Unit of Work +~~~~~~~~~~~~~~~~ + +Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a +typical implementation of the +`Unit of Work pattern `_, +to keep track of all the things that need to be done the next time +``flush`` is invoked. You usually do not directly interact with a +``UnitOfWork`` but with the ``EntityManager`` instead. + + diff --git a/vendor/doctrine/orm/docs/en/reference/association-mapping.rst b/vendor/doctrine/orm/docs/en/reference/association-mapping.rst new file mode 100644 index 00000000000..c212e57a5fc --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/association-mapping.rst @@ -0,0 +1,1094 @@ +Association Mapping +=================== + +This chapter explains mapping associations between objects. + +Instead of working with foreign keys in your code, you will always work with +references to objects instead and Doctrine will convert those references +to foreign keys internally. + +- A reference to a single object is represented by a foreign key. +- A collection of objects is represented by many foreign keys pointing to the object holding the collection + +This chapter is split into three different sections. + +- A list of all the possible association mapping use-cases is given. +- :ref:`association_mapping_defaults` are explained that simplify the use-case examples. +- :ref:`collections` are introduced that contain entities in associations. + +To gain a full understanding of associations you should also read about :doc:`owning and +inverse sides of associations ` + +Many-To-One, Unidirectional +--------------------------- + +A many-to-one association is the most common association between objects. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + + +.. note:: + + The above ``@JoinColumn`` is optional as it would default + to ``address_id`` and ``id`` anyways. You can omit it and let it + use the defaults. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + address_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + CREATE TABLE Address ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id); + +One-To-One, Unidirectional +-------------------------- + +Here is an example of a one-to-one association with a ``Product`` entity that +references one ``Shipping`` entity. The ``Shipping`` does not reference back to +the ``Product`` so that the reference is said to be unidirectional, in one +direction only. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + joinColumn: + name: shipping_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Product ( + id INT AUTO_INCREMENT NOT NULL, + shipping_id INT DEFAULT NULL, + UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipping_id), + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Shipping ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Product ADD FOREIGN KEY (shipping_id) REFERENCES Shipping(id); + +One-To-One, Bidirectional +------------------------- + +Here is a one-to-one relationship between a ``Customer`` and a +``Cart``. The ``Cart`` has a reference back to the ``Customer`` so +it is bidirectional. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + .. code-block:: yaml + + Customer: + oneToOne: + cart: + targetEntity: Cart + mappedBy: customer + Cart: + oneToOne: + customer: + targetEntity: Customer + inversedBy: cart + joinColumn: + name: customer_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Cart ( + id INT AUTO_INCREMENT NOT NULL, + customer_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Customer ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Cart ADD FOREIGN KEY (customer_id) REFERENCES Customer(id); + +See how the foreign key is defined on the owning side of the +relation, the table ``Cart``. + +One-To-One, Self-referencing +---------------------------- + +You can define a self-referencing one-to-one relationships like +below. + +.. code-block:: php + + features = new ArrayCollection(); + } + } + + /** @Entity **/ + class Feature + { + // ... + /** + * @ManyToOne(targetEntity="Product", inversedBy="features") + * @JoinColumn(name="product_id", referencedColumnName="id") + **/ + private $product; + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToMany: + features: + targetEntity: Feature + mappedBy: product + Feature: + type: entity + manyToOne: + product: + targetEntity: Product + inversedBy: features + joinColumn: + name: product_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Product ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Feature ( + id INT AUTO_INCREMENT NOT NULL, + product_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id); + +One-To-Many, Unidirectional with Join Table +------------------------------------------- + +A unidirectional one-to-many association can be mapped through a +join table. From Doctrine's point of view, it is simply mapped as a +unidirectional many-to-many whereby a unique constraint on one of +the join columns enforces the one-to-many cardinality. + +The following example sets up such a unidirectional one-to-many association: + +.. configuration-block:: + + .. code-block:: php + + phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity **/ + class Phonenumber + { + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + phonenumbers: + targetEntity: Phonenumber + joinTable: + name: users_phonenumbers + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + phonenumber_id: + referencedColumnName: id + unique: true + + +Generates the following MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + CREATE TABLE users_phonenumbers ( + user_id INT NOT NULL, + phonenumber_id INT NOT NULL, + UNIQUE INDEX users_phonenumbers_phonenumber_id_uniq (phonenumber_id), + PRIMARY KEY(user_id, phonenumber_id) + ) ENGINE = InnoDB; + + CREATE TABLE Phonenumber ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id); + +One-To-Many, Self-referencing +----------------------------- + +You can also setup a one-to-many association that is +self-referencing. In this example we setup a hierarchy of +``Category`` objects by creating a self referencing relationship. +This effectively models a hierarchy of categories and from the +database perspective is known as an adjacency list approach. + +.. configuration-block:: + + .. code-block:: php + + children = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + .. code-block:: xml + + + + + + + + + .. code-block:: yaml + + Category: + type: entity + oneToMany: + children: + targetEntity: Category + mappedBy: parent + manyToOne: + parent: + targetEntity: Category + inversedBy: children + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Category ( + id INT AUTO_INCREMENT NOT NULL, + parent_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Category ADD FOREIGN KEY (parent_id) REFERENCES Category(id); + +Many-To-Many, Unidirectional +---------------------------- + +Real many-to-many associations are less common. The following +example shows a unidirectional association between User and Group +entities: + +.. configuration-block:: + + .. code-block:: php + + groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** @Entity **/ + class Group + { + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE users_groups ( + user_id INT NOT NULL, + group_id INT NOT NULL, + PRIMARY KEY(user_id, group_id) + ) ENGINE = InnoDB; + CREATE TABLE Group ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE users_groups ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE users_groups ADD FOREIGN KEY (group_id) REFERENCES Group(id); + +.. note:: + + Why are many-to-many associations less common? Because + frequently you want to associate additional attributes with an + association, in which case you introduce an association class. + Consequently, the direct many-to-many association disappears and is + replaced by one-to-many/many-to-one associations between the 3 + participating classes. + +Many-To-Many, Bidirectional +--------------------------- + +Here is a similar many-to-many relationship as above except this +one is bidirectional. + +.. configuration-block:: + + .. code-block:: php + + groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity **/ + class Group + { + // ... + /** + * @ManyToMany(targetEntity="User", mappedBy="groups") + **/ + private $users; + + public function __construct() { + $this->users = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + inversedBy: users + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + + Group: + type: entity + manyToMany: + users: + targetEntity: User + mappedBy: groups + +The MySQL schema is exactly the same as for the Many-To-Many +uni-directional case above. + +Owning and Inverse Side on a ManyToMany association +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Many-To-Many associations you can chose which entity is the +owning and which the inverse side. There is a very simple semantic +rule to decide which side is more suitable to be the owning side +from a developers perspective. You only have to ask yourself, which +entity is responsible for the connection management and pick that +as the owning side. + +Take an example of two entities ``Article`` and ``Tag``. Whenever +you want to connect an Article to a Tag and vice-versa, it is +mostly the Article that is responsible for this relation. Whenever +you add a new article, you want to connect it with existing or new +tags. Your create Article form will probably support this notion +and allow to specify the tags directly. This is why you should pick +the Article as owning side, as it makes the code more +understandable: + +.. code-block:: php + + addArticle($this); // synchronously updating inverse side + $this->tags[] = $tag; + } + } + + class Tag + { + private $articles; + + public function addArticle(Article $article) + { + $this->articles[] = $article; + } + } + +This allows to group the tag adding on the ``Article`` side of the +association: + +.. code-block:: php + + addTag($tagA); + $article->addTag($tagB); + +Many-To-Many, Self-referencing +------------------------------ + +You can even have a self-referencing many-to-many association. A +common scenario is where a ``User`` has friends and the target +entity of that relationship is a ``User`` so it is self +referencing. In this example it is bidirectional so ``User`` has a +field named ``$friendsWithMe`` and ``$myFriends``. + +.. code-block:: php + + friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection(); + $this->myFriends = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE friends ( + user_id INT NOT NULL, + friend_user_id INT NOT NULL, + PRIMARY KEY(user_id, friend_user_id) + ) ENGINE = InnoDB; + ALTER TABLE friends ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE friends ADD FOREIGN KEY (friend_user_id) REFERENCES User(id); + +.. _association_mapping_defaults: + +Mapping Defaults +---------------- + +The ``@JoinColumn`` and ``@JoinTable`` definitions are usually optional and have +sensible default values. The defaults for a join column in a +one-to-one/many-to-one association is as follows: + +:: + + name: "_id" + referencedColumnName: "id" + +As an example, consider this mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + +This is essentially the same as the following, more verbose, +mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + joinColumn: + name: shipping_id + referencedColumnName: id + +The @JoinTable definition used for many-to-many mappings has +similar defaults. As an example, consider this mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + +This is essentially the same as the following, more verbose, mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + joinTable: + name: User_Group + joinColumns: + User_id: + referencedColumnName: id + inverseJoinColumns: + Group_id: + referencedColumnName: id + +In that case, the name of the join table defaults to a combination +of the simple, unqualified class names of the participating +classes, separated by an underscore character. The names of the +join columns default to the simple, unqualified class name of the +targeted class followed by "\_id". The referencedColumnName always +defaults to "id", just as in one-to-one or many-to-one mappings. + +If you accept these defaults, you can reduce the mapping code to a +minimum. + +.. _collections: + +Collections +----------- + +Unfortunately, PHP arrays, while being great for many things, are missing +features that make them suitable for lazy loading in the context of an ORM. +This is why in all the examples of many-valued associations in this manual we +will make use of a ``Collection`` interface and its +default implementation ``ArrayCollection`` that are both defined in the +``Doctrine\Common\Collections`` namespace. A collection implements +the PHP interfaces ``ArrayAccess``, ``Traversable`` and ``Countable``. + +.. note:: + + The Collection interface and ArrayCollection class, + like everything else in the Doctrine namespace, are neither part of + the ORM, nor the DBAL, it is a plain PHP class that has no outside + dependencies apart from dependencies on PHP itself (and the SPL). + Therefore using this class in your model and elsewhere + does not introduce a coupling to the ORM. + +Initializing Collections +------------------------ + +You should always initialize the collections of your ``@OneToMany`` +and ``@ManyToMany`` associations in the constructor of your entities: + +.. code-block:: php + + groups = new ArrayCollection(); + } + + public function getGroups() + { + return $this->groups; + } + } + +The following code will then work even if the Entity hasn't +been associated with an EntityManager yet: + +.. code-block:: php + + getGroups()->add($group); diff --git a/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst b/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst new file mode 100644 index 00000000000..4ece67666bb --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst @@ -0,0 +1,499 @@ +Basic Mapping +============= + +This guide explains the basic mapping of entities and properties. +After working through this guide you should know: + +- How to create PHP objects that can be saved to the database with Doctrine; +- How to configure the mapping between columns on tables and properties on + entities; +- What Doctrine mapping types are; +- Defining primary keys and how identifiers are generated by Doctrine; +- How quoting of reserved symbols works in Doctrine. + +Mapping of associations will be covered in the next chapter on +:doc:`Association Mapping `. + +Guide Assumptions +----------------- + +You should have already :doc:`installed and configure ` +Doctrine. + +Creating Classes for the Database +--------------------------------- + +Every PHP object that you want to save in the database using Doctrine +is called an "Entity". The term "Entity" describes objects +that have an identity over many independent requests. This identity is +usually achieved by assigning a unique identifier to an entity. +In this tutorial the following ``Message`` PHP class will serve as the +example Entity: + +.. code-block:: php + + ` +- :doc:`XML ` +- :doc:`YAML ` +- :doc:`PHP code ` + +This manual will usually show mapping metadata via docblock annotations, though +many examples also show the equivalent configuration in YAML and XML. + +.. note:: + + All metadata drivers perform equally. Once the metadata of a class has been + read from the source (annotations, xml or yaml) it is stored in an instance + of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are + stored in the metadata cache. If you're not using a metadata cache (not + recommended!) then the XML driver is the fastest. + +Marking our ``Message`` class as an entity for Doctrine is straightforward: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + Message: + type: entity + # ... + +With no additional information, Doctrine expects the entity to be saved +into a table with the same name as the class in our case ``Message``. +You can change this by configuring information about the table: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + Message: + type: entity + table: message + # ... + +Now the class ``Message`` will be saved and fetched from the table ``message``. + +Property Mapping +---------------- + +The next step after marking a PHP class as an entity is mapping its properties +to columns in a table. + +To configure a property use the ``@Column`` docblock annotation. The ``type`` +attribute specifies the :ref:`Doctrine Mapping Type ` +to use for the field. If the type is not specified, ``string`` is used as the +default. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Message: + type: entity + fields: + id: + type: integer + text: + length: 140 + postedAt: + type: datetime + column: posted_at + +When we don't explicitly specify a column name via the ``name`` option, Doctrine +assumes the field name is also the column name. This means that: + +* the ``id`` property will map to the column ``id`` using the type ``integer``; +* the ``text`` property will map to the column ``text`` with the default mapping type ``string``; +* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type. + +The Column annotation has some more attributes. Here is a complete +list: + +- ``type``: (optional, defaults to 'string') The mapping type to + use for the column. +- ``name``: (optional, defaults to field name) The name of the + column in the database. +- ``length``: (optional, default 255) The length of the column in + the database. (Applies only if a string-valued column is used). +- ``unique``: (optional, default FALSE) Whether the column is a + unique key. +- ``nullable``: (optional, default FALSE) Whether the database + column is nullable. +- ``precision``: (optional, default 0) The precision for a decimal + (exact numeric) column (applies only for decimal column), + which is the maximum number of digits that are stored for the values. +- ``scale``: (optional, default 0) The scale for a decimal (exact + numeric) column (applies only for decimal column), which represents + the number of digits to the right of the decimal point and must + not be greater than *precision*. +- ``columnDefinition``: (optional) Allows to define a custom + DDL snippet that is used to create the column. Warning: This normally + confuses the SchemaTool to always detect the column as changed. +- ``options``: (optional) Key-value pairs of options that get passed + to the underlying database platform when generating DDL statements. + +.. _reference-mapping-types: + +Doctrine Mapping Types +---------------------- + +The ``type`` option used in the ``@Column`` accepts any of the existing +Doctrine types or even your own custom types. A Doctrine type defines +the conversion between PHP and SQL types, independent from the database vendor +you are using. All Mapping Types that ship with Doctrine are fully portable +between the supported database systems. + +As an example, the Doctrine Mapping Type ``string`` defines the +mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. +depending on the RDBMS brand). Here is a quick overview of the +built-in mapping types: + +- ``string``: Type that maps a SQL VARCHAR to a PHP string. +- ``integer``: Type that maps a SQL INT to a PHP integer. +- ``smallint``: Type that maps a database SMALLINT to a PHP + integer. +- ``bigint``: Type that maps a database BIGINT to a PHP string. +- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean. +- ``decimal``: Type that maps a SQL DECIMAL to a PHP string. +- ``date``: Type that maps a SQL DATETIME to a PHP DateTime + object. +- ``time``: Type that maps a SQL TIME to a PHP DateTime object. +- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP + DateTime object. +- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP + DateTime object with timezone. +- ``text``: Type that maps a SQL CLOB to a PHP string. +- ``object``: Type that maps a SQL CLOB to a PHP object using + ``serialize()`` and ``unserialize()`` +- ``array``: Type that maps a SQL CLOB to a PHP array using + ``serialize()`` and ``unserialize()`` +- ``simple_array``: Type that maps a SQL CLOB to a PHP array using + ``implode()`` and ``explode()``, with a comma as delimiter. *IMPORTANT* + Only use this type if you are sure that your values cannot contain a ",". +- ``json_array``: Type that maps a SQL CLOB to a PHP array using + ``json_encode()`` and ``json_decode()`` +- ``float``: Type that maps a SQL Float (Double Precision) to a + PHP double. *IMPORTANT*: Works only with locale settings that use + decimal points as separator. +- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to + varchar but uses a specific type if the platform supports it. +- ``blob``: Type that maps a SQL BLOB to a PHP resource stream + +A cookbook article shows how to define :doc:`your own custom mapping types +<../cookbook/custom-mapping-types>`. + +.. note:: + + DateTime and Object types are compared by reference, not by value. Doctrine + updates this values if the reference changes and therefore behaves as if + these objects are immutable value objects. + +.. warning:: + + All Date types assume that you are exclusively using the default timezone + set by `date_default_timezone_set() `_ + or by the php.ini configuration ``date.timezone``. Working with + different timezones will cause troubles and unexpected behavior. + + If you need specific timezone handling you have to handle this + in your domain, converting all the values back and forth from UTC. + There is also a :doc:`cookbook entry <../cookbook/working-with-datetime>` + on working with datetimes that gives hints for implementing + multi timezone applications. + +Identifiers / Primary Keys +-------------------------- + +Every entity class must have an identifier/primary key. You can select +the field that serves as the identifier with the ``@Id`` +annotation. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + Message: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + # fields here + +In most cases using the automatic generator strategy (``@GeneratedValue``) is +what you want. It defaults to the identifier generation mechanism your current +database vendor prefers: AUTO_INCREMENT with MySQL, SERIAL with PostgreSQL, +Sequences with Oracle and so on. + +Identifier Generation Strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The previous example showed how to use the default identifier +generation strategy without knowing the underlying database with +the AUTO-detection strategy. It is also possible to specify the +identifier generation strategy more explicitly, which allows you to +make use of some additional features. + +Here is the list of possible generation strategies: + +- ``AUTO`` (default): Tells Doctrine to pick the strategy that is + preferred by the used database platform. The preferred strategies + are IDENTITY for MySQL, SQLite, MsSQL and SQL Anywhere and SEQUENCE + for Oracle and PostgreSQL. This strategy provides full portability. +- ``SEQUENCE``: Tells Doctrine to use a database sequence for ID + generation. This strategy does currently not provide full + portability. Sequences are supported by Oracle, PostgreSql and + SQL Anywhere. +- ``IDENTITY``: Tells Doctrine to use special identity columns in + the database that generate a value on insertion of a row. This + strategy does currently not provide full portability and is + supported by the following platforms: MySQL/SQLite/SQL Anywhere + (AUTO\_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL). +- ``TABLE``: Tells Doctrine to use a separate table for ID + generation. This strategy provides full portability. + ***This strategy is not yet implemented!*** +- ``NONE``: Tells Doctrine that the identifiers are assigned (and + thus generated) by your code. The assignment must take place before + a new entity is passed to ``EntityManager#persist``. NONE is the + same as leaving off the @GeneratedValue entirely. + +Sequence Generator +^^^^^^^^^^^^^^^^^^ + +The Sequence Generator can currently be used in conjunction with +Oracle or Postgres and allows some additional configuration options +besides specifying the sequence's name: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + Message: + type: entity + id: + id: + type: integer + generator: + strategy: SEQUENCE + sequenceGenerator: + sequenceName: message_seq + allocationSize: 100 + initialValue: 1 + +The initial value specifies at which value the sequence should +start. + +The allocationSize is a powerful feature to optimize INSERT +performance of Doctrine. The allocationSize specifies by how much +values the sequence is incremented whenever the next value is +retrieved. If this is larger than 1 (one) Doctrine can generate +identifier values for the allocationSizes amount of entities. In +the above example with ``allocationSize=100`` Doctrine 2 would only +need to access the sequence once to generate the identifiers for +100 new entities. + +*The default allocationSize for a @SequenceGenerator is currently 10.* + +.. caution:: + + The allocationSize is detected by SchemaTool and + transformed into an "INCREMENT BY " clause in the CREATE SEQUENCE + statement. For a database schema created manually (and not + SchemaTool) you have to make sure that the allocationSize + configuration option is never larger than the actual sequences + INCREMENT BY value, otherwise you may get duplicate keys. + + +.. note:: + + It is possible to use strategy="AUTO" and at the same time + specifying a @SequenceGenerator. In such a case, your custom + sequence settings are used in the case where the preferred strategy + of the underlying platform is SEQUENCE, such as for Oracle and + PostgreSQL. + + +Composite Keys +~~~~~~~~~~~~~~ + +with Doctrine 2 you can use composite primary keys, using ``@Id`` on more then +one column. Some restrictions exist opposed to using a single identifier in +this case: The use of the ``@GeneratedValue`` annotation is not supported, +which means you can only use composite keys if you generate the primary key +values yourself before calling ``EntityManager#persist()`` on the entity. + +More details on composite primary keys are discussed in a :doc:`dedicated tutorial +<../tutorials/composite-primary-keys>`. + +Quoting Reserved Words +---------------------- + +Sometimes it is necessary to quote a column or table name because of reserved +word conflicts. Doctrine does not quote identifiers automatically, because it +leads to more problems than it would solve. Quoting tables and column names +needs to be done explicitly using ticks in the definition. + +.. code-block:: php + + setQuoteStrategy(new AnsiQuoteStrategy()); diff --git a/vendor/doctrine/orm/docs/en/reference/batch-processing.rst b/vendor/doctrine/orm/docs/en/reference/batch-processing.rst new file mode 100644 index 00000000000..5d3cf188781 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/batch-processing.rst @@ -0,0 +1,187 @@ +Batch Processing +================ + +This chapter shows you how to accomplish bulk inserts, updates and +deletes with Doctrine in an efficient way. The main problem with +bulk operations is usually not to run out of memory and this is +especially what the strategies presented here provide help with. + +.. warning:: + + An ORM tool is not primarily well-suited for mass + inserts, updates or deletions. Every RDBMS has its own, most + effective way of dealing with such operations and if the options + outlined below are not sufficient for your purposes we recommend + you use the tools for your particular RDBMS for these bulk + operations. + + +Bulk Inserts +------------ + +Bulk inserts in Doctrine are best performed in batches, taking +advantage of the transactional write-behind behavior of an +``EntityManager``. The following code shows an example for +inserting 10000 objects with a batch size of 20. You may need to +experiment with the batch size to find the size that works best for +you. Larger batch sizes mean more prepared statement reuse +internally but also mean more work during ``flush``. + +.. code-block:: php + + setStatus('user'); + $user->setUsername('user' . $i); + $user->setName('Mr.Smith-' . $i); + $em->persist($user); + if (($i % $batchSize) === 0) { + $em->flush(); + $em->clear(); // Detaches all objects from Doctrine! + } + } + $em->flush(); //Persist objects that did not make up an entire batch + $em->clear(); + +Bulk Updates +------------ + +There are 2 possibilities for bulk updates with Doctrine. + +DQL UPDATE +~~~~~~~~~~ + +The by far most efficient way for bulk updates is to use a DQL +UPDATE query. Example: + +.. code-block:: php + + createQuery('update MyProject\Model\Manager m set m.salary = m.salary * 0.9'); + $numUpdated = $q->execute(); + +Iterating results +~~~~~~~~~~~~~~~~~ + +An alternative solution for bulk updates is to use the +``Query#iterate()`` facility to iterate over the query results step +by step instead of loading the whole result into memory at once. +The following example shows how to do this, combining the iteration +with the batching strategy that was already used for bulk inserts: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + foreach ($iterableResult as $row) { + $user = $row[0]; + $user->increaseCredit(); + $user->calculateNewBonuses(); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all updates. + $em->clear(); // Detaches all objects from Doctrine! + } + ++$i; + } + $em->flush(); + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + +.. note:: + + Results may be fully buffered by the database client/ connection allocating + additional memory not visible to the PHP process. For large sets this + may easily kill the process for no apparant reason. + + +Bulk Deletes +------------ + +There are two possibilities for bulk deletes with Doctrine. You can +either issue a single DQL DELETE query or you can iterate over +results removing them one at a time. + +DQL DELETE +~~~~~~~~~~ + +The by far most efficient way for bulk deletes is to use a DQL +DELETE query. + +Example: + +.. code-block:: php + + createQuery('delete from MyProject\Model\Manager m where m.salary > 100000'); + $numDeleted = $q->execute(); + +Iterating results +~~~~~~~~~~~~~~~~~ + +An alternative solution for bulk deletes is to use the +``Query#iterate()`` facility to iterate over the query results step +by step instead of loading the whole result into memory at once. +The following example shows how to do this: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + while (($row = $iterableResult->next()) !== false) { + $em->remove($row[0]); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all deletions. + $em->clear(); // Detaches all objects from Doctrine! + } + ++$i; + } + $em->flush(); + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + + +Iterating Large Results for Data-Processing +------------------------------------------- + +You can use the ``iterate()`` method just to iterate over a large +result and no UPDATE or DELETE intention. The ``IterableResult`` +instance returned from ``$query->iterate()`` implements the +Iterator interface so you can process a large result without memory +problems using the following approach: + +.. code-block:: php + + _em->createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + foreach ($iterableResult as $row) { + // do stuff with the data in the row, $row[0] is always the object + + // detach from Doctrine, so that it can be Garbage-Collected immediately + $this->_em->detach($row[0]); + } + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + + + diff --git a/vendor/doctrine/orm/docs/en/reference/best-practices.rst b/vendor/doctrine/orm/docs/en/reference/best-practices.rst new file mode 100644 index 00000000000..6937e99b18d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/best-practices.rst @@ -0,0 +1,111 @@ +Best Practices +============== + +The best practices mentioned here that affect database +design generally refer to best practices when working with Doctrine +and do not necessarily reflect best practices for database design +in general. + +Constrain relationships as much as possible +------------------------------------------- + +It is important to constrain relationships as much as possible. +This means: + + +- Impose a traversal direction (avoid bidirectional associations + if possible) +- Eliminate nonessential associations + +This has several benefits: + + +- Reduced coupling in your domain model +- Simpler code in your domain model (no need to maintain + bidirectionality properly) +- Less work for Doctrine + +Avoid composite keys +-------------------- + +Even though Doctrine fully supports composite keys it is best not +to use them if possible. Composite keys require additional work by +Doctrine and thus have a higher probability of errors. + +Use events judiciously +---------------------- + +The event system of Doctrine is great and fast. Even though making +heavy use of events, especially lifecycle events, can have a +negative impact on the performance of your application. Thus you +should use events judiciously. + +Use cascades judiciously +------------------------ + +Automatic cascades of the persist/remove/merge/etc. operations are +very handy but should be used wisely. Do NOT simply add all +cascades to all associations. Think about which cascades actually +do make sense for you for a particular association, given the +scenarios it is most likely used in. + +Don't use special characters +---------------------------- + +Avoid using any non-ASCII characters in class, field, table or +column names. Doctrine itself is not unicode-safe in many places +and will not be until PHP itself is fully unicode-aware (PHP6). + +Don't use identifier quoting +---------------------------- + +Identifier quoting is a workaround for using reserved words that +often causes problems in edge cases. Do not use identifier quoting +and avoid using reserved words as table or column names. + +Initialize collections in the constructor +----------------------------------------- + +It is recommended best practice to initialize any business +collections in entities in the constructor. Example: + +.. code-block:: php + + addresses = new ArrayCollection; + $this->articles = new ArrayCollection; + } + } + +Don't map foreign keys to fields in an entity +--------------------------------------------- + +Foreign keys have no meaning whatsoever in an object model. Foreign +keys are how a relational database establishes relationships. Your +object model establishes relationships through object references. +Thus mapping foreign keys to object fields heavily leaks details of +the relational model into the object model, something you really +should not do. + +Use explicit transaction demarcation +------------------------------------ + +While Doctrine will automatically wrap all DML operations in a +transaction on flush(), it is considered best practice to +explicitly set the transaction boundaries yourself. Otherwise every +single query is wrapped in a small transaction (Yes, SELECT +queries, too) since you can not talk to your database outside of a +transaction. While such short transactions for read-only (SELECT) +queries generally don't have any noticeable performance impact, it +is still preferable to use fewer, well-defined transactions that +are established through explicit transaction boundaries. + + diff --git a/vendor/doctrine/orm/docs/en/reference/caching.rst b/vendor/doctrine/orm/docs/en/reference/caching.rst new file mode 100644 index 00000000000..68813493c00 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/caching.rst @@ -0,0 +1,420 @@ +Caching +======= + +Doctrine provides cache drivers in the ``Common`` package for some +of the most popular caching implementations such as APC, Memcache +and Xcache. We also provide an ``ArrayCache`` driver which stores +the data in a PHP array. Obviously, when using ``ArrayCache``, the +cache does not persist between requests, but this is useful for +testing in a development environment. + +Cache Drivers +------------- + +The cache drivers follow a simple interface that is defined in +``Doctrine\Common\Cache\Cache``. All the cache drivers extend a +base class ``Doctrine\Common\Cache\AbstractCache`` which implements +this interface. + +The interface defines the following public methods for you to implement: + + +- fetch($id) - Fetches an entry from the cache +- contains($id) - Test if an entry exists in the cache +- save($id, $data, $lifeTime = false) - Puts data into the cache +- delete($id) - Deletes a cache entry + +Each driver extends the ``AbstractCache`` class which defines a few +abstract protected methods that each of the drivers must +implement: + + +- \_doFetch($id) +- \_doContains($id) +- \_doSave($id, $data, $lifeTime = false) +- \_doDelete($id) + +The public methods ``fetch()``, ``contains()`` etc. use the +above protected methods which are implemented by the drivers. The +code is organized this way so that the protected methods in the +drivers do the raw interaction with the cache implementation and +the ``AbstractCache`` can build custom functionality on top of +these methods. + +APC +~~~ + +In order to use the APC cache driver you must have it compiled and +enabled in your php.ini. You can read about APC +`in the PHP Documentation `_. It will give +you a little background information about what it is and how you +can use it as well as how to install it. + +Below is a simple example of how you could use the APC cache driver +by itself. + +.. code-block:: php + + save('cache_id', 'my_data'); + +Memcache +~~~~~~~~ + +In order to use the Memcache cache driver you must have it compiled +and enabled in your php.ini. You can read about Memcache +`on the PHP website `_. It will +give you a little background information about what it is and how +you can use it as well as how to install it. + +Below is a simple example of how you could use the Memcache cache +driver by itself. + +.. code-block:: php + + connect('memcache_host', 11211); + + $cacheDriver = new \Doctrine\Common\Cache\MemcacheCache(); + $cacheDriver->setMemcache($memcache); + $cacheDriver->save('cache_id', 'my_data'); + +Memcached +~~~~~~~~ + +Memcached is a more recent and complete alternative extension to +Memcache. + +In order to use the Memcached cache driver you must have it compiled +and enabled in your php.ini. You can read about Memcached +`on the PHP website `_. It will +give you a little background information about what it is and how +you can use it as well as how to install it. + +Below is a simple example of how you could use the Memcached cache +driver by itself. + +.. code-block:: php + + addServer('memcache_host', 11211); + + $cacheDriver = new \Doctrine\Common\Cache\MemcachedCache(); + $cacheDriver->setMemcached($memcached); + $cacheDriver->save('cache_id', 'my_data'); + +Xcache +~~~~~~ + +In order to use the Xcache cache driver you must have it compiled +and enabled in your php.ini. You can read about Xcache +`here `_. It will give you a little +background information about what it is and how you can use it as +well as how to install it. + +Below is a simple example of how you could use the Xcache cache +driver by itself. + +.. code-block:: php + + save('cache_id', 'my_data'); + +Redis +~~~~~ + +In order to use the Redis cache driver you must have it compiled +and enabled in your php.ini. You can read about what Redis is +`from here `_. Also check +`A PHP extension for Redis `_ for how you can use +and install the Redis PHP extension. + +Below is a simple example of how you could use the Redis cache +driver by itself. + +.. code-block:: php + + connect('redis_host', 6379); + + $cacheDriver = new \Doctrine\Common\Cache\RedisCache(); + $cacheDriver->setRedis($redis); + $cacheDriver->save('cache_id', 'my_data'); + +Using Cache Drivers +------------------- + +In this section we'll describe how you can fully utilize the API of +the cache drivers to save data to a cache, check if some cached data +exists, fetch the cached data and delete the cached data. We'll use the +``ArrayCache`` implementation as our example here. + +.. code-block:: php + + save('cache_id', 'my_data'); + +The ``save()`` method accepts three arguments which are described +below: + + +- ``$id`` - The cache id +- ``$data`` - The cache entry/data. +- ``$lifeTime`` - The lifetime. If != false, sets a specific + lifetime for this cache entry (null => infinite lifeTime). + +You can save any type of data whether it be a string, array, +object, etc. + +.. code-block:: php + + 'value1', + 'key2' => 'value2' + ); + $cacheDriver->save('my_array', $array); + +Checking +~~~~~~~~ + +Checking whether cached data exists is very simple: just use the +``contains()`` method. It accepts a single argument which is the ID +of the cache entry. + +.. code-block:: php + + contains('cache_id')) { + echo 'cache exists'; + } else { + echo 'cache does not exist'; + } + +Fetching +~~~~~~~~ + +Now if you want to retrieve some cache entry you can use the +``fetch()`` method. It also accepts a single argument just like +``contains()`` which is again the ID of the cache entry. + +.. code-block:: php + + fetch('my_array'); + +Deleting +~~~~~~~~ + +As you might guess, deleting is just as easy as saving, checking +and fetching. You can delete by an individual ID, or you can +delete all entries. + +By Cache ID +^^^^^^^^^^^ + +.. code-block:: php + + delete('my_array'); + +All +^^^ + +If you simply want to delete all cache entries you can do so with +the ``deleteAll()`` method. + +.. code-block:: php + + deleteAll(); + +Namespaces +~~~~~~~~~~ + +If you heavily use caching in your application and use it in +multiple parts of your application, or use it in different +applications on the same server you may have issues with cache +naming collisions. This can be worked around by using namespaces. +You can set the namespace a cache driver should use by using the +``setNamespace()`` method. + +.. code-block:: php + + setNamespace('my_namespace_'); + +Integrating with the ORM +------------------------ + +The Doctrine ORM package is tightly integrated with the cache +drivers to allow you to improve the performance of various aspects of +Doctrine by simply making some additional configurations and +method calls. + +Query Cache +~~~~~~~~~~~ + +It is highly recommended that in a production environment you cache +the transformation of a DQL query to its SQL counterpart. It +doesn't make sense to do this parsing multiple times as it doesn't +change unless you alter the DQL query. + +This can be done by configuring the query cache implementation to +use on your ORM configuration. + +.. code-block:: php + + setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Result Cache +~~~~~~~~~~~~ + +The result cache can be used to cache the results of your queries +so that we don't have to query the database or hydrate the data +again after the first time. You just need to configure the result +cache implementation. + +.. code-block:: php + + setResultCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Now when you're executing DQL queries you can configure them to use +the result cache. + +.. code-block:: php + + createQuery('select u from \Entities\User u'); + $query->useResultCache(true); + +You can also configure an individual query to use a different +result cache driver. + +.. code-block:: php + + setResultCacheDriver(new \Doctrine\Common\Cache\ApcCache()); + +.. note:: + + Setting the result cache driver on the query will + automatically enable the result cache for the query. If you want to + disable it pass false to ``useResultCache()``. + + :: + + useResultCache(false); + + +If you want to set the time the cache has to live you can use the +``setResultCacheLifetime()`` method. + +.. code-block:: php + + setResultCacheLifetime(3600); + +The ID used to store the result set cache is a hash which is +automatically generated for you if you don't set a custom ID +yourself with the ``setResultCacheId()`` method. + +.. code-block:: php + + setResultCacheId('my_custom_id'); + +You can also set the lifetime and cache ID by passing the values as +the second and third argument to ``useResultCache()``. + +.. code-block:: php + + useResultCache(true, 3600, 'my_custom_id'); + +Metadata Cache +~~~~~~~~~~~~~~ + +Your class metadata can be parsed from a few different sources like +YAML, XML, Annotations, etc. Instead of parsing this information on +each request we should cache it using one of the cache drivers. + +Just like the query and result cache we need to configure it +first. + +.. code-block:: php + + setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Now the metadata information will only be parsed once and stored in +the cache driver. + +Clearing the Cache +------------------ + +We've already shown you how you can use the API of the +cache drivers to manually delete cache entries. For your +convenience we offer command line tasks to help you with +clearing the query, result and metadata cache. + +From the Doctrine command line you can run the following commands: + +To clear the query cache use the ``orm:clear-cache:query`` task. + +.. code-block:: php + + $ ./doctrine orm:clear-cache:query + +To clear the metadata cache use the ``orm:clear-cache:metadata`` task. + +.. code-block:: php + + $ ./doctrine orm:clear-cache:metadata + +To clear the result cache use the ``orm:clear-cache:result`` task. + +.. code-block:: php + + $ ./doctrine orm:clear-cache:result + +All these tasks accept a ``--flush`` option to flush the entire +contents of the cache instead of invalidating the entries. + +Cache Slams +----------- + +Something to be careful of when using the cache drivers is +"cache slams". Imagine you have a heavily trafficked website with some +code that checks for the existence of a cache record and if it does +not exist it generates the information and saves it to the cache. +Now, if 100 requests were issued all at the same time and each one +sees the cache does not exist and they all try to insert the same +cache entry it could lock up APC, Xcache, etc. and cause problems. +Ways exist to work around this, like pre-populating your cache and +not letting your users' requests populate the cache. + +You can read more about cache slams +`in this blog post `_. + + diff --git a/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst b/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst new file mode 100644 index 00000000000..d0f099895fb --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst @@ -0,0 +1,151 @@ +Change Tracking Policies +======================== + +Change tracking is the process of determining what has changed in +managed entities since the last time they were synchronized with +the database. + +Doctrine provides 3 different change tracking policies, each having +its particular advantages and disadvantages. The change tracking +policy can be defined on a per-class basis (or more precisely, +per-hierarchy). + +Deferred Implicit +~~~~~~~~~~~~~~~~~ + +The deferred implicit policy is the default change tracking policy +and the most convenient one. With this policy, Doctrine detects the +changes by a property-by-property comparison at commit time and +also detects changes to entities or new entities that are +referenced by other managed entities ("persistence by +reachability"). Although the most convenient policy, it can have +negative effects on performance if you are dealing with large units +of work (see "Understanding the Unit of Work"). Since Doctrine +can't know what has changed, it needs to check all managed entities +for changes every time you invoke EntityManager#flush(), making +this operation rather costly. + +Deferred Explicit +~~~~~~~~~~~~~~~~~ + +The deferred explicit policy is similar to the deferred implicit +policy in that it detects changes through a property-by-property +comparison at commit time. The difference is that Doctrine 2 only +considers entities that have been explicitly marked for change detection +through a call to EntityManager#persist(entity) or through a save +cascade. All other entities are skipped. This policy therefore +gives improved performance for larger units of work while +sacrificing the behavior of "automatic dirty checking". + +Therefore, flush() operations are potentially cheaper with this +policy. The negative aspect this has is that if you have a rather +large application and you pass your objects through several layers +for processing purposes and business tasks you may need to track +yourself which entities have changed on the way so you can pass +them to EntityManager#persist(). + +This policy can be configured as follows: + +.. code-block:: php + + _listeners[] = $listener; + } + } + +Then, in each property setter of this class or derived classes, you +need to notify all the ``PropertyChangedListener`` instances. As an +example we add a convenience method on ``MyEntity`` that shows this +behaviour: + +.. code-block:: php + + _listeners) { + foreach ($this->_listeners as $listener) { + $listener->propertyChanged($this, $propName, $oldValue, $newValue); + } + } + } + + public function setData($data) + { + if ($data != $this->data) { + $this->_onPropertyChanged('data', $this->data, $data); + $this->data = $data; + } + } + } + +You have to invoke ``_onPropertyChanged`` inside every method that +changes the persistent state of ``MyEntity``. + +The check whether the new value is different from the old one is +not mandatory but recommended. That way you also have full control +over when you consider a property changed. + +The negative point of this policy is obvious: You need implement an +interface and write some plumbing code. But also note that we tried +hard to keep this notification functionality abstract. Strictly +speaking, it has nothing to do with the persistence layer and the +Doctrine ORM or DBAL. You may find that property notification +events come in handy in many other scenarios as well. As mentioned +earlier, the ``Doctrine\Common`` namespace is not that evil and +consists solely of very small classes and interfaces that have +almost no external dependencies (none to the DBAL and none to the +ORM) and that you can easily take with you should you want to swap +out the persistence layer. This change tracking policy does not +introduce a dependency on the Doctrine DBAL/ORM or the persistence +layer. + +The positive point and main advantage of this policy is its +effectiveness. It has the best performance characteristics of the 3 +policies with larger units of work and a flush() operation is very +cheap when nothing has changed. + + diff --git a/vendor/doctrine/orm/docs/en/reference/configuration.rst b/vendor/doctrine/orm/docs/en/reference/configuration.rst new file mode 100644 index 00000000000..5837f0f956d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/configuration.rst @@ -0,0 +1,141 @@ +Installation and Configuration +============================== + +Doctrine can be installed with `Composer `_. For +older versions we still have `PEAR packages +`_. + +Define the following requirement in your ``composer.json`` file: + +:: + + { + "require": { + "doctrine/orm": "*" + } + } + +Then call ``composer install`` from your command line. If you don't know +how Composer works, check out their `Getting Started +`_ to set up. + +Class loading +------------- + +Autoloading is taken care of by Composer. You just have to include the composer autoload file in your project: + +.. code-block:: php + + 'pdo_mysql', + 'user' => 'root', + 'password' => '', + 'dbname' => 'foo', + ); + + $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode); + $entityManager = EntityManager::create($dbParams, $config); + +Or if you prefer XML: + +.. code-block:: php + + ` section. + +.. note:: + + You can learn more about the database connection configuration in the + `Doctrine DBAL connection configuration reference `_. + +Setting up the Commandline Tool +------------------------------- + +Doctrine ships with a number of command line tools that are very helpful +during development. You can call this command from the Composer binary +directory: + +.. code-block:: sh + + $ php vendor/bin/doctrine + +You need to register your applications EntityManager to the console tool +to make use of the tasks by creating a ``cli-config.php`` file with the +following content: + +On Doctrine 2.4 and above: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); diff --git a/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst b/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst new file mode 100644 index 00000000000..ea5f60deb92 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst @@ -0,0 +1,1696 @@ +Doctrine Query Language +=========================== + +DQL stands for Doctrine Query Language and is an Object +Query Language derivate that is very similar to the Hibernate +Query Language (HQL) or the Java Persistence Query Language (JPQL). + +In essence, DQL provides powerful querying capabilities over your +object model. Imagine all your objects lying around in some storage +(like an object database). When writing DQL queries, think about +querying that storage to pick a certain subset of your objects. + +.. note:: + + A common mistake for beginners is to mistake DQL for + being just some form of SQL and therefore trying to use table names + and column names or join arbitrary tables together in a query. You + need to think about DQL as a query language for your object model, + not for your relational schema. + + +DQL is case in-sensitive, except for namespace, class and field +names, which are case sensitive. + +Types of DQL queries +-------------------- + +DQL as a query language has SELECT, UPDATE and DELETE constructs +that map to their corresponding SQL statement types. INSERT +statements are not allowed in DQL, because entities and their +relations have to be introduced into the persistence context +through ``EntityManager#persist()`` to ensure consistency of your +object model. + +DQL SELECT statements are a very powerful way of retrieving parts +of your domain model that are not accessible via associations. +Additionally they allow to retrieve entities and their associations +in one single SQL select statement which can make a huge difference +in performance in contrast to using several queries. + +DQL UPDATE and DELETE statements offer a way to execute bulk +changes on the entities of your domain model. This is often +necessary when you cannot load all the affected entities of a bulk +update into memory. + +SELECT queries +-------------- + +DQL SELECT clause +~~~~~~~~~~~~~~~~~ + +The select clause of a DQL query specifies what appears in the +query result. The composition of all the expressions in the select +clause also influences the nature of the query result. + +Here is an example that selects all users with an age > 20: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u WHERE u.age > 20'); + $users = $query->getResult(); + +Lets examine the query: + + +- ``u`` is a so called identification variable or alias that + refers to the ``MyProject\Model\User`` class. By placing this alias + in the SELECT clause we specify that we want all instances of the + User class that are matched by this query to appear in the query + result. +- The FROM keyword is always followed by a fully-qualified class + name which in turn is followed by an identification variable or + alias for that class name. This class designates a root of our + query from which we can navigate further via joins (explained + later) and path expressions. +- The expression ``u.age`` in the WHERE clause is a path + expression. Path expressions in DQL are easily identified by the + use of the '.' operator that is used for constructing paths. The + path expression ``u.age`` refers to the ``age`` field on the User + class. + +The result of this query would be a list of User objects where all +users are older than 20. + +The SELECT clause allows to specify both class identification +variables that signal the hydration of a complete entity class or +just fields of the entity using the syntax ``u.name``. Combinations +of both are also allowed and it is possible to wrap both fields and +identification values into aggregation and DQL functions. Numerical +fields can be part of computations using mathematical operations. +See the sub-section on `Functions, Operators, Aggregates`_ for +more information. + +Joins +~~~~~ + +A SELECT query can contain joins. There are 2 types of JOINs: +"Regular" Joins and "Fetch" Joins. + +**Regular Joins**: Used to limit the results and/or compute +aggregate values. + +**Fetch Joins**: In addition to the uses of regular joins: Used to +fetch related entities and include them in the hydrated result of a +query. + +There is no special DQL keyword that distinguishes a regular join +from a fetch join. A join (be it an inner or outer join) becomes a +"fetch join" as soon as fields of the joined entity appear in the +SELECT part of the DQL query outside of an aggregate function. +Otherwise its a "regular join". + +Example: + +Regular join of the address: + +.. code-block:: php + + createQuery("SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin'"); + $users = $query->getResult(); + +Fetch join of the address: + +.. code-block:: php + + createQuery("SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'"); + $users = $query->getResult(); + +When Doctrine hydrates a query with fetch-join it returns the class +in the FROM clause on the root level of the result array. In the +previous example an array of User instances is returned and the +address of each user is fetched and hydrated into the +``User#address`` variable. If you access the address Doctrine does +not need to lazy load the association with another query. + +.. note:: + + Doctrine allows you to walk all the associations between + all the objects in your domain model. Objects that were not already + loaded from the database are replaced with lazy load proxy + instances. Non-loaded Collections are also replaced by lazy-load + instances that fetch all the contained objects upon first access. + However relying on the lazy-load mechanism leads to many small + queries executed against the database, which can significantly + affect the performance of your application. **Fetch Joins** are the + solution to hydrate most or all of the entities that you need in a + single SELECT query. + + +Named and Positional Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +DQL supports both named and positional parameters, however in +contrast to many SQL dialects positional parameters are specified +with numbers, for example "?1", "?2" and so on. Named parameters +are specified with ":name1", ":name2" and so on. + +When referencing the parameters in ``Query#setParameter($param, $value)`` +both named and positional parameters are used **without** their prefixes. + +DQL SELECT Examples +~~~~~~~~~~~~~~~~~~~ + +This section contains a large set of DQL queries and some +explanations of what is happening. The actual result also depends +on the hydration mode. + +Hydrate all User entities: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u'); + $users = $query->getResult(); // array of User objects + +Retrieve the IDs of all CmsUsers: + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u'); + $ids = $query->getResult(); // array of CmsUser ids + +Retrieve the IDs of all users that have written an article: + +.. code-block:: php + + createQuery('SELECT DISTINCT u.id FROM CmsArticle a JOIN a.user u'); + $ids = $query->getResult(); // array of CmsUser ids + +Retrieve all articles and sort them by the name of the articles +users instance: + +.. code-block:: php + + createQuery('SELECT a FROM CmsArticle a JOIN a.user u ORDER BY u.name ASC'); + $articles = $query->getResult(); // array of CmsArticle objects + +Retrieve the Username and Name of a CmsUser: + +.. code-block:: php + + createQuery('SELECT u.username, u.name FROM CmsUser u'); + $users = $query->getResult(); // array of CmsUser username and name values + echo $users[0]['username']; + +Retrieve a ForumUser and his single associated entity: + +.. code-block:: php + + createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a'); + $users = $query->getResult(); // array of ForumUser objects with the avatar association loaded + echo get_class($users[0]->getAvatar()); + +Retrieve a CmsUser and fetch join all the phonenumbers he has: + +.. code-block:: php + + createQuery('SELECT u, p FROM CmsUser u JOIN u.phonenumbers p'); + $users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded + $phonenumbers = $users[0]->getPhonenumbers(); + +Hydrate a result in Ascending: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u ORDER BY u.id ASC'); + $users = $query->getResult(); // array of ForumUser objects + +Or in Descending Order: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC'); + $users = $query->getResult(); // array of ForumUser objects + +Using Aggregate Functions: + +.. code-block:: php + + createQuery('SELECT COUNT(u.id) FROM Entities\User u'); + $count = $query->getSingleScalarResult(); + + $query = $em->createQuery('SELECT u, count(g.id) FROM Entities\User u JOIN u.groups g GROUP BY u.id'); + $result = $query->getResult(); + +With WHERE Clause and Positional Parameter: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE u.id = ?1'); + $query->setParameter(1, 321); + $users = $query->getResult(); // array of ForumUser objects + +With WHERE Clause and Named Parameter: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE u.username = :name'); + $query->setParameter('name', 'Bob'); + $users = $query->getResult(); // array of ForumUser objects + +With Nested Conditions in WHERE Clause: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id'); + $query->setParameters(array( + 'name' => 'Bob', + 'name2' => 'Alice', + 'id' => 321, + )); + $users = $query->getResult(); // array of ForumUser objects + +With COUNT DISTINCT: + +.. code-block:: php + + createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser'); + $users = $query->getResult(); // array of ForumUser objects + +With Arithmetic Expression in WHERE clause: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000'); + $users = $query->getResult(); // array of ForumUser objects + +Retrieve user entities with Arithmetic Expression in ORDER clause, using the ``HIDDEN`` keyword: + +.. code-block:: php + + createQuery('SELECT u, u.posts_count + u.likes_count AS HIDDEN score FROM CmsUser u ORDER BY score'); + $users = $query->getResult(); // array of User objects + +Using a LEFT JOIN to hydrate all user-ids and optionally associated +article-ids: + +.. code-block:: php + + createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a'); + $results = $query->getResult(); // array of user ids and every article_id for each user + +Restricting a JOIN clause by additional conditions: + +.. code-block:: php + + createQuery("SELECT u FROM CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE :foo"); + $query->setParameter('foo', '%foo%'); + $users = $query->getResult(); + +Using several Fetch JOINs: + +.. code-block:: php + + createQuery('SELECT u, a, p, c FROM CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c'); + $users = $query->getResult(); + +BETWEEN in WHERE clause: + +.. code-block:: php + + createQuery('SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ?1 AND ?2'); + $query->setParameter(1, 123); + $query->setParameter(2, 321); + $usernames = $query->getResult(); + +DQL Functions in WHERE clause: + +.. code-block:: php + + createQuery("SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'"); + $usernames = $query->getResult(); + +IN() Expression: + +.. code-block:: php + + createQuery('SELECT u.name FROM CmsUser u WHERE u.id IN(46)'); + $usernames = $query->getResult(); + + $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id IN (1, 2)'); + $users = $query->getResult(); + + $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id NOT IN (1)'); + $users = $query->getResult(); + +CONCAT() DQL Function: + +.. code-block:: php + + createQuery("SELECT u.id FROM CmsUser u WHERE CONCAT(u.name, 's') = ?1"); + $query->setParameter(1, 'Jess'); + $ids = $query->getResult(); + + $query = $em->createQuery('SELECT CONCAT(u.id, u.name) FROM CmsUser u WHERE u.id = ?1'); + $query->setParameter(1, 321); + $idUsernames = $query->getResult(); + +EXISTS in WHERE clause with correlated Subquery + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.user = u.id)'); + $ids = $query->getResult(); + +Get all users who are members of $group. + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u WHERE :groupId MEMBER OF u.groups'); + $query->setParameter('groupId', $group); + $ids = $query->getResult(); + +Get all users that have more than 1 phonenumber + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE SIZE(u.phonenumbers) > 1'); + $users = $query->getResult(); + +Get all users that have no phonenumber + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY'); + $users = $query->getResult(); + +Get all instances of a specific type, for use with inheritance +hierarchies: + +.. versionadded:: 2.1 + +.. code-block:: php + + createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee'); + $query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1'); + $query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1'); + +Get all users visible on a given website that have chosen certain gender: + +.. versionadded:: 2.2 + +.. code-block:: php + + createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)'); + +.. versionadded:: 2.4 + +Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys: + +.. code-block:: php + + createQuery("SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1"); + +Joins between entities without associations were not possible until version +2.4, where you can generate an arbitrary join with the following syntax: + +.. code-block:: php + + createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email'); + +Partial Object Syntax +^^^^^^^^^^^^^^^^^^^^^ + +By default when you run a DQL query in Doctrine and select only a +subset of the fields for a given entity, you do not receive objects +back. Instead, you receive only arrays as a flat rectangular result +set, similar to how you would if you were just using SQL directly +and joining some data. + +If you want to select partial objects you can use the ``partial`` +DQL keyword: + +.. code-block:: php + + createQuery('SELECT partial u.{id, username} FROM CmsUser u'); + $users = $query->getResult(); // array of partially loaded CmsUser objects + +You use the partial syntax when joining as well: + +.. code-block:: php + + createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a'); + $users = $query->getResult(); // array of partially loaded CmsUser objects + +"NEW" Operator Syntax +^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.4 + +Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries. + +- When using ``SELECT NEW`` you don't need to specify a mapped entity. +- You can specify any PHP class, it's only require that the constructor of this class matches the ``NEW`` statement. +- This approach involves determining exactly which columns you really need, + and instantiating data-transfer object that containing a constructor with those arguments. + +If you want to select data-transfer objects you should create a class: + +.. code-block:: php + + createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city) FROM Customer c JOIN c.email e JOIN c.address a'); + $users = $query->getResult(); // array of CustomerDTO + +.. code-block:: php + + createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c'); + $users = $query->getResult(); // array of CustomerDTO + +Note that you can only pass scalar expressions to the constructor. + +Using INDEX BY +~~~~~~~~~~~~~~ + +The INDEX BY construct is nothing that directly translates into SQL +but that affects object and array hydration. After each FROM and +JOIN clause you specify by which field this class should be indexed +in the result. By default a result is incremented by numerical keys +starting with 0. However with INDEX BY you can specify any other +column to be the key of your result, it really only makes sense +with primary or unique fields though: + +.. code-block:: sql + + SELECT u.id, u.status, upper(u.name) nameUpper FROM User u INDEX BY u.id + JOIN u.phonenumbers p INDEX BY p.phonenumber + +Returns an array of the following kind, indexed by both user-id +then phonenumber-id: + +.. code-block:: php + + array + 0 => + array + 1 => + object(stdClass)[299] + public '__CLASS__' => string 'Doctrine\Tests\Models\CMS\CmsUser' (length=33) + public 'id' => int 1 + .. + 'nameUpper' => string 'ROMANB' (length=6) + 1 => + array + 2 => + object(stdClass)[298] + public '__CLASS__' => string 'Doctrine\Tests\Models\CMS\CmsUser' (length=33) + public 'id' => int 2 + ... + 'nameUpper' => string 'JWAGE' (length=5) + +UPDATE queries +-------------- + +DQL not only allows to select your Entities using field names, you +can also execute bulk updates on a set of entities using an +DQL-UPDATE query. The Syntax of an UPDATE query works as expected, +as the following example shows: + +.. code-block:: sql + + UPDATE MyProject\Model\User u SET u.password = 'new' WHERE u.id IN (1, 2, 3) + +References to related entities are only possible in the WHERE +clause and using sub-selects. + +.. warning:: + + DQL UPDATE statements are ported directly into a + Database UPDATE statement and therefore bypass any locking scheme, events + and do not increment the version column. Entities that are already + loaded into the persistence context will *NOT* be synced with the + updated database state. It is recommended to call + ``EntityManager#clear()`` and retrieve new instances of any + affected entity. + + +DELETE queries +-------------- + +DELETE queries can also be specified using DQL and their syntax is +as simple as the UPDATE syntax: + +.. code-block:: sql + + DELETE MyProject\Model\User u WHERE u.id = 4 + +The same restrictions apply for the reference of related entities. + +.. warning:: + + DQL DELETE statements are ported directly into a + Database DELETE statement and therefore bypass any events and checks for the + version column if they are not explicitly added to the WHERE clause + of the query. Additionally Deletes of specifies entities are *NOT* + cascaded to related entities even if specified in the metadata. + + +Functions, Operators, Aggregates +-------------------------------- + +DQL Functions +~~~~~~~~~~~~~ + +The following functions are supported in SELECT, WHERE and HAVING +clauses: + + +- IDENTITY(single\_association\_path\_expression [, fieldMapping]) - Retrieve the foreign key column of association of the owning side +- ABS(arithmetic\_expression) +- CONCAT(str1, str2) +- CURRENT\_DATE() - Return the current date +- CURRENT\_TIME() - Returns the current time +- CURRENT\_TIMESTAMP() - Returns a timestamp of the current date + and time. +- LENGTH(str) - Returns the length of the given string +- LOCATE(needle, haystack [, offset]) - Locate the first + occurrence of the substring in the string. +- LOWER(str) - returns the string lowercased. +- MOD(a, b) - Return a MOD b. +- SIZE(collection) - Return the number of elements in the + specified collection +- SQRT(q) - Return the square-root of q. +- SUBSTRING(str, start [, length]) - Return substring of given + string. +- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim + the string by the given trim char, defaults to whitespaces. +- UPPER(str) - Return the upper-case of the given string. +- DATE_ADD(date, days, unit) - Add the number of days to a given date. (Supported units are DAY, MONTH) +- DATE_SUB(date, days, unit) - Substract the number of days from a given date. (Supported units are DAY, MONTH) +- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2. + +Arithmetic operators +~~~~~~~~~~~~~~~~~~~~ + +You can do math in DQL using numeric values, for example: + +.. code-block:: sql + + SELECT person.salary * 1.5 FROM CompanyPerson person WHERE person.salary < 100000 + +Aggregate Functions +~~~~~~~~~~~~~~~~~~~ + +The following aggregate functions are allowed in SELECT and GROUP +BY clauses: AVG, COUNT, MIN, MAX, SUM + +Other Expressions +~~~~~~~~~~~~~~~~~ + +DQL offers a wide-range of additional expressions that are known +from SQL, here is a list of all the supported constructs: + + +- ``ALL/ANY/SOME`` - Used in a WHERE clause followed by a + sub-select this works like the equivalent constructs in SQL. +- ``BETWEEN a AND b`` and ``NOT BETWEEN a AND b`` can be used to + match ranges of arithmetic values. +- ``IN (x1, x2, ...)`` and ``NOT IN (x1, x2, ..)`` can be used to + match a set of given values. +- ``LIKE ..`` and ``NOT LIKE ..`` match parts of a string or text + using % as a wildcard. +- ``IS NULL`` and ``IS NOT NULL`` to check for null values +- ``EXISTS`` and ``NOT EXISTS`` in combination with a sub-select + +Adding your own functions to the DQL language +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default DQL comes with functions that are part of a large basis +of underlying databases. However you will most likely choose a +database platform at the beginning of your project and most likely +never change it. For this cases you can easily extend the DQL +parser with own specialized platform functions. + +You can register custom DQL functions in your ORM Configuration: + +.. code-block:: php + + addCustomStringFunction($name, $class); + $config->addCustomNumericFunction($name, $class); + $config->addCustomDatetimeFunction($name, $class); + + $em = EntityManager::create($dbParams, $config); + +The functions have to return either a string, numeric or datetime +value depending on the registered function type. As an example we +will add a MySQL specific FLOOR() functionality. All the given +classes have to implement the base class : + +.. code-block:: php + + walkSimpleArithmeticExpression( + $this->simpleArithmeticExpression + ) . ')'; + } + + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + } + +We will register the function by calling and can then use it: + +.. code-block:: php + + getConfiguration(); + $config->registerNumericFunction('FLOOR', 'MyProject\Query\MysqlFloor'); + + $dql = "SELECT FLOOR(person.salary * 1.75) FROM CompanyPerson person"; + +Querying Inherited Classes +-------------------------- + +This section demonstrates how you can query inherited classes and +what type of results to expect. + +Single Table +~~~~~~~~~~~~ + +`Single Table Inheritance `_ +is an inheritance mapping strategy where all classes of a hierarchy +are mapped to a single database table. In order to distinguish +which row represents which type in the hierarchy a so-called +discriminator column is used. + +First we need to setup an example set of entities to use. In this +scenario it is a generic Person and Employee example: + +.. code-block:: php + + setName('test'); + $employee->setDepartment('testing'); + $em->persist($employee); + $em->flush(); + +Now lets run a simple query to retrieve the ``Employee`` we just +created: + +.. code-block:: sql + + SELECT e FROM Entities\Employee e WHERE e.name = 'test' + +If we check the generated SQL you will notice it has some special +conditions added to ensure that we will only get back ``Employee`` +entities: + +.. code-block:: sql + + SELECT p0_.id AS id0, p0_.name AS name1, p0_.department AS department2, + p0_.discr AS discr3 FROM Person p0_ + WHERE (p0_.name = ?) AND p0_.discr IN ('employee') + +Class Table Inheritance +~~~~~~~~~~~~~~~~~~~~~~~ + +`Class Table Inheritance `_ +is an inheritance mapping strategy where each class in a hierarchy +is mapped to several tables: its own table and the tables of all +parent classes. The table of a child class is linked to the table +of a parent class through a foreign key constraint. Doctrine 2 +implements this strategy through the use of a discriminator column +in the topmost table of the hierarchy because this is the easiest +way to achieve polymorphic queries with Class Table Inheritance. + +The example for class table inheritance is the same as single +table, you just need to change the inheritance type from +``SINGLE_TABLE`` to ``JOINED``: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + + // example2: using setDql + $q = $em->createQuery(); + $q->setDql('select u from MyProject\Model\User u'); + +Query Result Formats +~~~~~~~~~~~~~~~~~~~~ + +The format in which the result of a DQL SELECT query is returned +can be influenced by a so-called ``hydration mode``. A hydration +mode specifies a particular way in which a SQL result set is +transformed. Each hydration mode has its own dedicated method on +the Query class. Here they are: + + +- ``Query#getResult()``: Retrieves a collection of objects. The + result is either a plain collection of objects (pure) or an array + where the objects are nested in the result rows (mixed). +- ``Query#getSingleResult()``: Retrieves a single object. If the + result contains more than one object, an ``NonUniqueResultException`` + is thrown. If the result contains no objects, an ``NoResultException`` + is thrown. The pure/mixed distinction does not apply. +- ``Query#getOneOrNullResult()``: Retrieve a single object. If no + object is found null will be returned. +- ``Query#getArrayResult()``: Retrieves an array graph (a nested + array) that is largely interchangeable with the object graph + generated by ``Query#getResult()`` for read-only purposes. + + .. note:: + + An array graph can differ from the corresponding object + graph in certain scenarios due to the difference of the identity + semantics between arrays and objects. + + + +- ``Query#getScalarResult()``: Retrieves a flat/rectangular result + set of scalar values that can contain duplicate data. The + pure/mixed distinction does not apply. +- ``Query#getSingleScalarResult()``: Retrieves a single scalar + value from the result returned by the dbms. If the result contains + more than a single scalar value, an exception is thrown. The + pure/mixed distinction does not apply. + +Instead of using these methods, you can alternatively use the +general-purpose method +``Query#execute(array $params = array(), $hydrationMode = Query::HYDRATE_OBJECT)``. +Using this method you can directly supply the hydration mode as the +second parameter via one of the Query constants. In fact, the +methods mentioned earlier are just convenient shortcuts for the +execute method. For example, the method ``Query#getResult()`` +internally invokes execute, passing in ``Query::HYDRATE_OBJECT`` as +the hydration mode. + +The use of the methods mentioned earlier is generally preferred as +it leads to more concise code. + +Pure and Mixed Results +~~~~~~~~~~~~~~~~~~~~~~ + +The nature of a result returned by a DQL SELECT query retrieved +through ``Query#getResult()`` or ``Query#getArrayResult()`` can be +of 2 forms: **pure** and **mixed**. In the previous simple +examples, you already saw a "pure" query result, with only objects. +By default, the result type is **pure** but +**as soon as scalar values, such as aggregate values or other scalar values that do not belong to an entity, appear in the SELECT part of the DQL query, the result becomes mixed**. +A mixed result has a different structure than a pure result in +order to accommodate for the scalar values. + +A pure result usually looks like this: + +.. code-block:: php + + $dql = "SELECT u FROM User u"; + + array + [0] => Object + [1] => Object + [2] => Object + ... + +A mixed result on the other hand has the following general +structure: + +.. code-block:: php + + $dql = "SELECT u, 'some scalar string', count(u.groups) AS num FROM User u JOIN u.groups g GROUP BY u.id"; + + array + [0] + [0] => Object + [1] => "some scalar string" + ['num'] => 42 + // ... more scalar values, either indexed numerically or with a name + [1] + [0] => Object + [1] => "some scalar string" + ['num'] => 42 + // ... more scalar values, either indexed numerically or with a name + +To better understand mixed results, consider the following DQL +query: + +.. code-block:: sql + + SELECT u, UPPER(u.name) nameUpper FROM MyProject\Model\User u + +This query makes use of the ``UPPER`` DQL function that returns a +scalar value and because there is now a scalar value in the SELECT +clause, we get a mixed result. + +Conventions for mixed results are as follows: + + +- The object fetched in the FROM clause is always positioned with the key '0'. +- Every scalar without a name is numbered in the order given in the query, starting with 1. +- Every aliased scalar is given with its alias-name as the key. The case of the name is kept. +- If several objects are fetched from the FROM clause they alternate every row. + + +Here is how the result could look like: + +.. code-block:: php + + array + array + [0] => User (Object) + ['nameUpper'] => "ROMAN" + array + [0] => User (Object) + ['nameUpper'] => "JONATHAN" + ... + +And here is how you would access it in PHP code: + +.. code-block:: php + + getName(); + echo "Name UPPER: " . $row['nameUpper']; + } + +Fetching Multiple FROM Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you fetch multiple entities that are listed in the FROM clause then the hydration +will return the rows iterating the different top-level entities. + +.. code-block:: php + + $dql = "SELECT u, g FROM User u, Group g"; + + array + [0] => Object (User) + [1] => Object (Group) + [2] => Object (User) + [3] => Object (Group) + + +Hydration Modes +~~~~~~~~~~~~~~~ + +Each of the Hydration Modes makes assumptions about how the result +is returned to user land. You should know about all the details to +make best use of the different result formats: + +The constants for the different hydration modes are: + + +- Query::HYDRATE\_OBJECT +- Query::HYDRATE\_ARRAY +- Query::HYDRATE\_SCALAR +- Query::HYDRATE\_SINGLE\_SCALAR + +Object Hydration +^^^^^^^^^^^^^^^^ + +Object hydration hydrates the result set into the object graph: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_OBJECT); + +Array Hydration +^^^^^^^^^^^^^^^ + +You can run the same query with array hydration and the result set +is hydrated into an array that represents the object graph: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_ARRAY); + +You can use the ``getArrayResult()`` shortcut as well: + +.. code-block:: php + + getArrayResult(); + +Scalar Hydration +^^^^^^^^^^^^^^^^ + +If you want to return a flat rectangular result set instead of an +object graph you can use scalar hydration: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_SCALAR); + echo $users[0]['u_id']; + +The following assumptions are made about selected fields using +Scalar Hydration: + + +1. Fields from classes are prefixed by the DQL alias in the result. + A query of the kind 'SELECT u.name ..' returns a key 'u\_name' in + the result rows. + +Single Scalar Hydration +^^^^^^^^^^^^^^^^^^^^^^^ + +If you have a query which returns just a single scalar value you can use +single scalar hydration: + +.. code-block:: php + + createQuery('SELECT COUNT(a.id) FROM CmsUser u LEFT JOIN u.articles a WHERE u.username = ?1 GROUP BY u.id'); + $query->setParameter(1, 'jwage'); + $numArticles = $query->getResult(Query::HYDRATE_SINGLE_SCALAR); + +You can use the ``getSingleScalarResult()`` shortcut as well: + +.. code-block:: php + + getSingleScalarResult(); + +Custom Hydration Modes +^^^^^^^^^^^^^^^^^^^^^^ + +You can easily add your own custom hydration modes by first +creating a class which extends ``AbstractHydrator``: + +.. code-block:: php + + _stmt->fetchAll(PDO::FETCH_ASSOC); + } + } + +Next you just need to add the class to the ORM configuration: + +.. code-block:: php + + getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator'); + +Now the hydrator is ready to be used in your queries: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $results = $query->getResult('CustomHydrator'); + +Iterating Large Result Sets +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are situations when a query you want to execute returns a +very large result-set that needs to be processed. All the +previously described hydration modes completely load a result-set +into memory which might not be feasible with large result sets. See +the `Batch Processing `_ section on details how +to iterate large result sets. + +Functions +~~~~~~~~~ + +The following methods exist on the ``AbstractQuery`` which both +``Query`` and ``NativeQuery`` extend from. + +Parameters +^^^^^^^^^^ + +Prepared Statements that use numerical or named wildcards require +additional parameters to be executable against the database. To +pass parameters to the query the following methods can be used: + + +- ``AbstractQuery::setParameter($param, $value)`` - Set the + numerical or named wildcard to the given value. +- ``AbstractQuery::setParameters(array $params)`` - Set an array + of parameter key-value pairs. +- ``AbstractQuery::getParameter($param)`` +- ``AbstractQuery::getParameters()`` + +Both named and positional parameters are passed to these methods without their ? or : prefix. + +Cache related API +^^^^^^^^^^^^^^^^^ + +You can cache query results based either on all variables that +define the result (SQL, Hydration Mode, Parameters and Hints) or on +user-defined cache keys. However by default query results are not +cached at all. You have to enable the result cache on a per query +basis. The following example shows a complete workflow using the +Result Cache API: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u WHERE u.id = ?1'); + $query->setParameter(1, 12); + + $query->setResultCacheDriver(new ApcCache()); + + $query->useResultCache(true) + ->setResultCacheLifeTime($seconds = 3600); + + $result = $query->getResult(); // cache miss + + $query->expireResultCache(true); + $result = $query->getResult(); // forced expire, cache miss + + $query->setResultCacheId('my_query_result'); + $result = $query->getResult(); // saved in given result cache id. + + // or call useResultCache() with all parameters: + $query->useResultCache(true, $seconds = 3600, 'my_query_result'); + $result = $query->getResult(); // cache hit! + + // Introspection + $queryCacheProfile = $query->getQueryCacheProfile(); + $cacheDriver = $query->getResultCacheDriver(); + $lifetime = $query->getLifetime(); + $key = $query->getCacheKey(); + +.. note:: + + You can set the Result Cache Driver globally on the + ``Doctrine\ORM\Configuration`` instance so that it is passed to + every ``Query`` and ``NativeQuery`` instance. + + +Query Hints +^^^^^^^^^^^ + +You can pass hints to the query parser and hydrators by using the +``AbstractQuery::setHint($name, $value)`` method. Currently there +exist mostly internal query hints that are not be consumed in +userland. However the following few hints are to be used in +userland: + + +- Query::HINT\_FORCE\_PARTIAL\_LOAD - Allows to hydrate objects + although not all their columns are fetched. This query hint can be + used to handle memory consumption problems with large result-sets + that contain char or binary data. Doctrine has no way of implicitly + reloading this data. Partially loaded objects have to be passed to + ``EntityManager::refresh()`` if they are to be reloaded fully from + the database. +- Query::HINT\_REFRESH - This query is used internally by + ``EntityManager::refresh()`` and can be used in userland as well. + If you specify this hint and a query returns the data for an entity + that is already managed by the UnitOfWork, the fields of the + existing entity will be refreshed. In normal operation a result-set + that loads data of an already existing entity is discarded in favor + of the already existing entity. +- Query::HINT\_CUSTOM\_TREE\_WALKERS - An array of additional + ``Doctrine\ORM\Query\TreeWalker`` instances that are attached to + the DQL query parsing process. + +Query Cache (DQL Query Only) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Parsing a DQL query and converting it into a SQL query against the +underlying database platform obviously has some overhead in +contrast to directly executing Native SQL queries. That is why +there is a dedicated Query Cache for caching the DQL parser +results. In combination with the use of wildcards you can reduce +the number of parsed queries in production to zero. + +The Query Cache Driver is passed from the +``Doctrine\ORM\Configuration`` instance to each +``Doctrine\ORM\Query`` instance by default and is also enabled by +default. This also means you don't regularly need to fiddle with +the parameters of the Query Cache, however if you do there are +several methods to interact with it: + + +- ``Query::setQueryCacheDriver($driver)`` - Allows to set a Cache + instance +- ``Query::setQueryCacheLifeTime($seconds = 3600)`` - Set lifetime + of the query caching. +- ``Query::expireQueryCache($bool)`` - Enforce the expiring of the + query cache if set to true. +- ``Query::getExpireQueryCache()`` +- ``Query::getQueryCacheDriver()`` +- ``Query::getQueryCacheLifeTime()`` + +First and Max Result Items (DQL Query Only) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can limit the number of results returned from a DQL query as +well as specify the starting offset, Doctrine then uses a strategy +of manipulating the select query to return only the requested +number of results: + + +- ``Query::setMaxResults($maxResults)`` +- ``Query::setFirstResult($offset)`` + +.. note:: + + If your query contains a fetch-joined collection + specifying the result limit methods are not working as you would + expect. Set Max Results restricts the number of database result + rows, however in the case of fetch-joined collections one root + entity might appear in many rows, effectively hydrating less than + the specified number of results. + +.. _dql-temporarily-change-fetch-mode: + +Temporarily change fetch mode in DQL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While normally all your associations are marked as lazy or extra lazy you will have cases where you are using DQL and don't want to +fetch join a second, third or fourth level of entities into your result, because of the increased cost of the SQL JOIN. You +can mark a many-to-one or one-to-one association as fetched temporarily to batch fetch these entities using a WHERE .. IN query. + +.. code-block:: php + + createQuery("SELECT u FROM MyProject\User u"); + $query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER); + $query->execute(); + +Given that there are 10 users and corresponding addresses in the database the executed queries will look something like: + +.. code-block:: sql + + SELECT * FROM users; + SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + +.. note:: + Changing the fetch mode during a query is only possible for one-to-one and many-to-one relations. + + +EBNF +---- + +The following context-free grammar, written in an EBNF variant, +describes the Doctrine Query Language. You can consult this grammar +whenever you are unsure about what is possible with DQL or what the +correct syntax for a particular query should be. + +Document syntax: +~~~~~~~~~~~~~~~~ + + +- non-terminals begin with an upper case character +- terminals begin with a lower case character +- parentheses (...) are used for grouping +- square brackets [...] are used for defining an optional part, + e.g. zero or one time +- curly brackets {...} are used for repetition, e.g. zero or more + times +- double quotation marks "..." define a terminal string +- a vertical bar \| represents an alternative + +Terminals +~~~~~~~~~ + + +- identifier (name, email, ...) +- string ('foo', 'bar''s house', '%ninja%', ...) +- char ('/', '\\', ' ', ...) +- integer (-1, 0, 1, 34, ...) +- float (-0.23, 0.007, 1.245342E+8, ...) +- boolean (false, true) + +Query Language +~~~~~~~~~~~~~~ + +.. code-block:: php + + QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement + +Statements +~~~~~~~~~~ + +.. code-block:: php + + SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + UpdateStatement ::= UpdateClause [WhereClause] + DeleteStatement ::= DeleteClause [WhereClause] + +Identifiers +~~~~~~~~~~~ + +.. code-block:: php + + /* Alias Identification usage (the "u" of "u.name") */ + IdentificationVariable ::= identifier + + /* Alias Identification declaration (the "u" of "FROM User u") */ + AliasIdentificationVariable :: = identifier + + /* identifier that must be a class name (the "User" of "FROM User u") */ + AbstractSchemaName ::= identifier + + /* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */ + AliasResultVariable = identifier + + /* ResultVariable identifier usage of mapped field aliases (the "total" of "COUNT(*) AS total") */ + ResultVariable = identifier + + /* identifier that must be a field (the "name" of "u.name") */ + /* This is responsible to know if the field exists in Object, no matter if it's a relation or a simple field */ + FieldIdentificationVariable ::= identifier + + /* identifier that must be a collection-valued association field (to-many) (the "Phonenumbers" of "u.Phonenumbers") */ + CollectionValuedAssociationField ::= FieldIdentificationVariable + + /* identifier that must be a single-valued association field (to-one) (the "Group" of "u.Group") */ + SingleValuedAssociationField ::= FieldIdentificationVariable + + /* identifier that must be an embedded class state field */ + EmbeddedClassStateField ::= FieldIdentificationVariable + + /* identifier that must be a simple state field (name, email, ...) (the "name" of "u.name") */ + /* The difference between this and FieldIdentificationVariable is only semantical, because it points to a single field (not mapping to a relation) */ + SimpleStateField ::= FieldIdentificationVariable + +Path Expressions +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /* "u.Group" or "u.Phonenumbers" declarations */ + JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) + + /* "u.Group" or "u.Phonenumbers" usages */ + AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + + /* "u.name" or "u.Group" */ + SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + + /* "u.name" or "u.Group.name" */ + StateFieldPathExpression ::= IdentificationVariable "." StateField + + /* "u.Group" */ + SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + + /* "u.Group.Permissions" */ + CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + + /* "name" */ + StateField ::= {EmbeddedClassStateField "."}* SimpleStateField + +Clauses +~~~~~~~ + +.. code-block:: php + + SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}* + SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* + DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable + FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* + SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + WhereClause ::= "WHERE" ConditionalExpression + HavingClause ::= "HAVING" ConditionalExpression + GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* + OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + +Items +~~~~~ + +.. code-block:: php + + UpdateItem ::= SingleValuedPathExpression "=" NewValue + OrderByItem ::= (SimpleArithmeticExpression | SingleValuedPathExpression | ScalarExpression | ResultVariable | FunctionDeclaration) ["ASC" | "DESC"] + GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression + NewValue ::= SimpleArithmeticExpression | "NULL" + +From, Join and Index by +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* + SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration + RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy] + Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration | RangeVariableDeclaration) ["WITH" ConditionalExpression] + IndexBy ::= "INDEX" "BY" StateFieldPathExpression + +Select Expressions +~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable] + SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable] + PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet + PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" + NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + NewObjectArg ::= ScalarExpression | "(" Subselect ")" + +Conditional Expressions +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + ConditionalFactor ::= ["NOT"] ConditionalPrimary + ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + SimpleConditionalExpression ::= ComparisonExpression | BetweenExpression | LikeExpression | + InExpression | NullComparisonExpression | ExistsExpression | + EmptyCollectionComparisonExpression | CollectionMemberExpression | + InstanceOfExpression + + +Collection Expressions +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + +Literal Values +~~~~~~~~~~~~~~ + +.. code-block:: php + + Literal ::= string | char | integer | float | boolean + InParameter ::= Literal | InputParameter + +Input Parameter +~~~~~~~~~~~~~~~ + +.. code-block:: php + + InputParameter ::= PositionalParameter | NamedParameter + PositionalParameter ::= "?" integer + NamedParameter ::= ":" string + +Arithmetic Expressions +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")" + | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings + | FunctionsReturningDatetime | IdentificationVariable | ResultVariable + | InputParameter | CaseExpression + +Scalar and Type Expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression + StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")" + StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression + BooleanExpression ::= BooleanPrimary | "(" Subselect ")" + BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter + EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + SimpleEntityExpression ::= IdentificationVariable | InputParameter + DatetimeExpression ::= DatetimePrimary | "(" Subselect ")" + DatetimePrimary ::= StateFieldPathExpression | InputParameter | FunctionsReturningDatetime | AggregateExpression + +.. note:: + + Parts of CASE expressions are not yet implemented. + +Aggregate Expressions +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")" + +Case Expressions +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + 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 ")" + +Other Expressions +~~~~~~~~~~~~~~~~~ + +QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS + +.. code-block:: php + + QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression + ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) + InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" + InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + InstanceOfParameter ::= AbstractSchemaName | InputParameter + LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] + NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" + ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" + +Functions +~~~~~~~~~ + +.. code-block:: php + + FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDateTime + + FunctionsReturningNumerics ::= + "LENGTH" "(" StringPrimary ")" | + "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" | + "ABS" "(" SimpleArithmeticExpression ")" | + "SQRT" "(" SimpleArithmeticExpression ")" | + "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + "SIZE" "(" CollectionValuedPathExpression ")" | + "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + + FunctionsReturningDateTime ::= + "CURRENT_DATE" | + "CURRENT_TIME" | + "CURRENT_TIMESTAMP" | + "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" | + "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + + FunctionsReturningStrings ::= + "CONCAT" "(" StringPrimary "," StringPrimary ")" | + "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + "LOWER" "(" StringPrimary ")" | + "UPPER" "(" StringPrimary ")" | + "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" + + diff --git a/vendor/doctrine/orm/docs/en/reference/events.rst b/vendor/doctrine/orm/docs/en/reference/events.rst new file mode 100644 index 00000000000..6ec2f092db3 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/events.rst @@ -0,0 +1,983 @@ +Events +====== + +Doctrine 2 features a lightweight event system that is part of the +Common package. Doctrine uses it to dispatch system events, mainly +:ref:`lifecycle events `. +You can also use it for your own custom events. + +The Event System +---------------- + +The event system is controlled by the ``EventManager``. It is the +central point of Doctrine's event listener system. Listeners are +registered on the manager and events are dispatched through the +manager. + +.. code-block:: php + + addEventListener(array(self::preFoo, self::postFoo), $this); + } + + public function preFoo(EventArgs $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(EventArgs $e) + { + $this->postFooInvoked = true; + } + } + + // Create a new instance + $test = new TestEvent($evm); + +Events can be dispatched by using the ``dispatchEvent()`` method. + +.. code-block:: php + + dispatchEvent(TestEvent::preFoo); + $evm->dispatchEvent(TestEvent::postFoo); + +You can easily remove a listener with the ``removeEventListener()`` +method. + +.. code-block:: php + + removeEventListener(array(self::preFoo, self::postFoo), $this); + +The Doctrine 2 event system also has a simple concept of event +subscribers. We can define a simple ``TestEventSubscriber`` class +which implements the ``\Doctrine\Common\EventSubscriber`` interface +and implements a ``getSubscribedEvents()`` method which returns an +array of events it should be subscribed to. + +.. code-block:: php + + preFooInvoked = true; + } + + public function getSubscribedEvents() + { + return array(TestEvent::preFoo); + } + } + + $eventSubscriber = new TestEventSubscriber(); + $evm->addEventSubscriber($eventSubscriber); + +.. note:: + + The array to return in the ``getSubscribedEvents`` method is a simple array + with the values being the event names. The subscriber must have a method + that is named exactly like the event. + +Now when you dispatch an event, any event subscribers will be +notified for that event. + +.. code-block:: php + + dispatchEvent(TestEvent::preFoo); + +Now you can test the ``$eventSubscriber`` instance to see if the +``preFoo()`` method was invoked. + +.. code-block:: php + + preFooInvoked) { + echo 'pre foo invoked!'; + } + +Naming convention +~~~~~~~~~~~~~~~~~ + +Events being used with the Doctrine 2 EventManager are best named +with camelcase and the value of the corresponding constant should +be the name of the constant itself, even with spelling. This has +several reasons: + + +- It is easy to read. +- Simplicity. +- Each method within an EventSubscriber is named after the + corresponding constant's value. If the constant's name and value differ + it contradicts the intention of using the constant and makes your code + harder to maintain. + +An example for a correct notation can be found in the example +``TestEvent`` above. + +.. _reference-events-lifecycle-events: + +Lifecycle Events +---------------- + +The EntityManager and UnitOfWork trigger a bunch of events during +the life-time of their registered entities. + + +- preRemove - The preRemove event occurs for a given entity before + the respective EntityManager remove operation for that entity is + executed. It is not called for a DQL DELETE statement. +- postRemove - The postRemove event occurs for an entity after the + entity has been deleted. It will be invoked after the database + delete operations. It is not called for a DQL DELETE statement. +- prePersist - The prePersist event occurs for a given entity + before the respective EntityManager persist operation for that + entity is executed. It should be noted that this event is only triggered on + *initial* persist of an entity (i.e. it does not trigger on future updates). +- postPersist - 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. +- preUpdate - The preUpdate event occurs before the database + update operations to entity data. It is not called for a DQL UPDATE statement. +- postUpdate - The postUpdate event occurs after the database + update operations to entity data. It is not called for a DQL UPDATE statement. +- postLoad - 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. +- loadClassMetadata - The loadClassMetadata event occurs after the + mapping metadata for a class has been loaded from a mapping source + (annotations/xml/yaml). This event is not a lifecycle callback. +- onClassMetadataNotFound - Loading class metadata for a particular + requested class name failed. Manipulating the given event args instance + allows providing fallback metadata even when no actual metadata exists + or could be found. This event is not a lifecycle callback. +- preFlush - The preFlush event occurs at the very beginning of a flush + operation. This event is not a lifecycle callback. +- onFlush - The onFlush event occurs after the change-sets of all + managed entities are computed. This event is not a lifecycle + callback. +- postFlush - The postFlush event occurs at the end of a flush operation. This + event is not a lifecycle callback. +- onClear - The onClear event occurs when the EntityManager#clear() operation is + invoked, after all references to entities have been removed from the unit of + work. This event is not a lifecycle callback. + +.. warning:: + + Note that, when using ``Doctrine\ORM\AbstractQuery#iterate()``, ``postLoad`` + events will be executed immediately after objects are being hydrated, and therefore + associations are not guaranteed to be initialized. It is not safe to combine + usage of ``Doctrine\ORM\AbstractQuery#iterate()`` and ``postLoad`` event + handlers. + +.. warning:: + + Note that the postRemove event or any events triggered after an entity removal + can receive an uninitializable proxy in case you have configured an entity to + cascade remove relations. In this case, you should load yourself the proxy in + the associated pre event. + +You can access the Event constants from the ``Events`` class in the +ORM package. + +.. code-block:: php + + createdAt = date('Y-m-d H:i:s'); + } + + /** @PrePersist */ + public function doOtherStuffOnPrePersist() + { + $this->value = 'changed from prePersist callback!'; + } + + /** @PostPersist */ + public function doStuffOnPostPersist() + { + $this->value = 'changed from postPersist callback!'; + } + + /** @PostLoad */ + public function doStuffOnPostLoad() + { + $this->value = 'changed from postLoad callback!'; + } + + /** @PreUpdate */ + public function doStuffOnPreUpdate() + { + $this->value = 'changed from preUpdate callback!'; + } + } + +Note that the methods set as lifecycle callbacks need to be public and, +when using these annotations, you have to apply the +``@HasLifecycleCallbacks`` marker annotation on the entity class. + +If you want to register lifecycle callbacks from YAML or XML you +can do it with the following. + +.. code-block:: yaml + + User: + type: entity + fields: + # ... + name: + type: string(50) + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ] + postPersist: [ doStuffOnPostPersist ] + +In YAML the ``key`` of the lifecycleCallbacks entry is the event that you +are triggering on and the value is the method (or methods) to call. The allowed +event types are the ones listed in the previous Lifecycle Events section. + +XML would look something like this: + +.. code-block:: xml + + + + + + + + + + + + + + + + +In XML the ``type`` of the lifecycle-callback entry is the event that you +are triggering on and the ``method`` is the method to call. The allowed event +types are the ones listed in the previous Lifecycle Events section. + +When using YAML or XML you need to remember to create public methods to match the +callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``, +``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be +defined on your ``User`` model. + +.. code-block:: php + + hasChangedField('username')) { + // Do something when the username is changed. + } + } + } + +Listening and subscribing to Lifecycle Events +--------------------------------------------- + +Lifecycle event listeners are much more powerful than the simple +lifecycle callbacks that are defined on the entity classes. They +sit at a level above the entities and allow you to implement re-usable +behaviors across different entity classes. + +Note that they require much more detailed knowledge about the inner +workings of the EntityManager and UnitOfWork. Please read the +*Implementing Event Listeners* section carefully if you are trying +to write your own listener. + +For event subscribers, there are no surprises. They declare the +lifecycle events in their ``getSubscribedEvents`` method and provide +public methods that expect the relevant arguments. + +A lifecycle event listener looks like the following: + +.. code-block:: php + + getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + } + +A lifecycle event subscriber may looks like this: + +.. code-block:: php + + getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + +.. note:: + + Lifecycle events are triggered for all entities. It is the responsibility + of the listeners and subscribers to check if the entity is of a type + it wants to handle. + +To register an event listener or subscriber, you have to hook it into the +EventManager that is passed to the EntityManager factory: + +.. code-block:: php + + addEventListener(array(Events::preUpdate), new MyEventListener()); + $eventManager->addEventSubscriber(new MyEventSubscriber()); + + $entityManager = EntityManager::create($dbOpts, $config, $eventManager); + +You can also retrieve the event manager instance after the +EntityManager was created: + +.. code-block:: php + + getEventManager()->addEventListener(array(Events::preUpdate), new MyEventListener()); + $entityManager->getEventManager()->addEventSubscriber(new MyEventSubscriber()); + +.. _reference-events-implementing-listeners: + +Implementing Event Listeners +---------------------------- + +This section explains what is and what is not allowed during +specific lifecycle events of the UnitOfWork. Although you get +passed the EntityManager in all of these events, you have to follow +these restrictions very carefully since operations in the wrong +event may produce lots of different errors, such as inconsistent +data and lost updates/persists/removes. + +For the described events that are also lifecycle callback events +the restrictions apply as well, with the additional restriction +that (prior to version 2.4) you do not have access to the +EntityManager or UnitOfWork APIs inside these events. + +prePersist +~~~~~~~~~~ + +There are two ways for the ``prePersist`` event to be triggered. +One is obviously when you call ``EntityManager#persist()``. The +event is also called for all cascaded associations. + +There is another way for ``prePersist`` to be called, inside the +``flush()`` method when changes to associations are computed and +this association is marked as cascade persist. Any new entity found +during this operation is also persisted and ``prePersist`` called +on it. This is called "persistence by reachability". + +In both cases you get passed a ``LifecycleEventArgs`` instance +which has access to the entity and the entity manager. + +The following restrictions apply to ``prePersist``: + + +- If you are using a PrePersist Identity Generator such as + sequences the ID value will *NOT* be available within any + PrePersist events. +- Doctrine will not recognize changes made to relations in a prePersist + event. This includes modifications to + collections such as additions, removals or replacement. + +preRemove +~~~~~~~~~ + +The ``preRemove`` event is called on every entity when its passed +to the ``EntityManager#remove()`` method. It is cascaded for all +associations that are marked as cascade delete. + +There are no restrictions to what methods can be called inside the +``preRemove`` event, except when the remove method itself was +called during a flush operation. + +preFlush +~~~~~~~~ + +``preFlush`` is called at ``EntityManager#flush()`` before +anything else. ``EntityManager#flush()`` can be called safely +inside its listeners. + +.. code-block:: php + + getEntityManager(); + $uow = $em->getUnitOfWork(); + + foreach ($uow->getScheduledEntityInsertions() as $entity) { + + } + + foreach ($uow->getScheduledEntityUpdates() as $entity) { + + } + + foreach ($uow->getScheduledEntityDeletions() as $entity) { + + } + + foreach ($uow->getScheduledCollectionDeletions() as $col) { + + } + + foreach ($uow->getScheduledCollectionUpdates() as $col) { + + } + } + } + +The following restrictions apply to the onFlush event: + + +- If you create and persist a new entity in ``onFlush``, then + calling ``EntityManager#persist()`` is not enough. + You have to execute an additional call to + ``$unitOfWork->computeChangeSet($classMetadata, $entity)``. +- Changing primitive fields or associations requires you to + explicitly trigger a re-computation of the changeset of the + affected entity. This can be done by calling + ``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``. + +postFlush +~~~~~~~~~ + +``postFlush`` is called at the end of ``EntityManager#flush()``. +``EntityManager#flush()`` can **NOT** be called safely inside its listeners. + +.. code-block:: php + + getEntity() instanceof User) { + if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') { + $eventArgs->setNewValue('name', 'Bob'); + } + } + } + } + +You could also use this listener to implement validation of all the +fields that have changed. This is more efficient than using a +lifecycle callback when there are expensive validations to call: + +.. code-block:: php + + getEntity() instanceof Account) { + if ($eventArgs->hasChangedField('creditCard')) { + $this->validateCreditCard($eventArgs->getNewValue('creditCard')); + } + } + } + + private function validateCreditCard($no) + { + // throw an exception to interrupt flush event. Transaction will be rolled back. + } + } + +Restrictions for this event: + + +- Changes to associations of the passed entities are not + recognized by the flush operation anymore. +- Changes to fields of the passed entities are not recognized by + the flush operation anymore, use the computed change-set passed to + the event to modify primitive field values, e.g. use + ``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above. +- Any calls to ``EntityManager#persist()`` or + ``EntityManager#remove()``, even in combination with the UnitOfWork + API are strongly discouraged and don't work as expected outside the + flush operation. + +postUpdate, postRemove, postPersist +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The three post events are called inside ``EntityManager#flush()``. +Changes in here are not relevant to the persistence in the +database, but you can use these events to alter non-persistable items, +like non-mapped fields, logging or even associated classes that are +directly mapped by Doctrine. + +postLoad +~~~~~~~~ + +This event is called after an entity is constructed by the +EntityManager. + +Entity listeners +---------------- + +.. versionadded:: 2.4 + +An entity listener is a lifecycle listener class used for an entity. + +- The entity listener's mapping may be applied to an entity class or mapped superclass. +- An entity listener is defined by mapping the entity class with the corresponding mapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + MyProject\Entity\User: + type: entity + entityListeners: + UserListener: + # .... + +.. _reference-entity-listeners: + +Entity listeners class +~~~~~~~~~~~~~~~~~~~~~~ + +An ``Entity Listener`` could be any class, by default it should be a class with a no-arg constructor. + +- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity +- An entity listener method receives two arguments, the entity instance and the lifecycle event. +- The callback method can be defined by naming convention or specifying a method mapping. +- When a listener mapping is not given the parser will use the naming convention to look for a matching method, + e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event. +- When a listener mapping is given the parser will not look for any methods using the naming convention. + +.. code-block:: php + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Entity\User: + type: entity + entityListeners: + UserListener: + preFlush: [preFlushHandler] + postLoad: [postLoadHandler] + + postPersist: [postPersistHandler] + prePersist: [prePersistHandler] + + postUpdate: [postUpdateHandler] + preUpdate: [preUpdateHandler] + + postRemove: [postRemoveHandler] + preRemove: [preRemoveHandler] + # .... + + + +Entity listeners resolver +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Doctrine invokes the listener resolver to get the listener instance. + +- A resolver allows you register a specific entity listener instance. +- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver`` + +Specifying an entity listener instance : + +.. code-block:: php + + service = $service; + } + + public function preUpdate(User $user, PreUpdateEventArgs $event) + { + $this->service->doSomething($user); + } + } + + // register a entity listener. + $listener = $container->get('user_listener'); + $em->getConfiguration()->getEntityListenerResolver()->register($listener); + +Implementing your own resolver : + +.. code-block:: php + + container = $container; + } + + public function resolve($className) + { + // resolve the service id by the given class name; + $id = 'user_listener'; + + return $this->container->get($id); + } + } + + // Configure the listener resolver only before instantiating the EntityManager + $configurations->setEntityListenerResolver(new MyEntityListenerResolver); + EntityManager::create(.., $configurations, ..); + +Load ClassMetadata Event +------------------------ + +When the mapping information for an entity is read, it is populated +in to a ``ClassMetadataInfo`` instance. You can hook in to this +process and manipulate the instance. + +.. code-block:: php + + getMetadataFactory(); + $evm = $em->getEventManager(); + $evm->addEventListener(Events::loadClassMetadata, $test); + + class TestEvent + { + public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs) + { + $classMetadata = $eventArgs->getClassMetadata(); + $fieldMapping = array( + 'fieldName' => 'about', + 'type' => 'string', + 'length' => 255 + ); + $classMetadata->mapField($fieldMapping); + } + } + + diff --git a/vendor/doctrine/orm/docs/en/reference/faq.rst b/vendor/doctrine/orm/docs/en/reference/faq.rst new file mode 100644 index 00000000000..45fde18d7e0 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/faq.rst @@ -0,0 +1,224 @@ +Frequently Asked Questions +========================== + +.. note:: + + This FAQ is a work in progress. We will add lots of questions and not answer them right away just to remember + what is often asked. If you stumble across an unanswered question please write a mail to the mailing-list or + join the #doctrine channel on Freenode IRC. + +Database Schema +--------------- + +How do I set the charset and collation for MySQL tables? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can't set these values inside the annotations, yml or xml mapping files. To make a database +work with the default charset and collation you should configure MySQL to use it as default charset, +or create the database with charset and collation details. This way they get inherited to all newly +created database tables and columns. + +Entity Classes +-------------- + +I access a variable and its null, what is wrong? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If this variable is a public variable then you are violating one of the criteria for entities. +All properties have to be protected or private for the proxy object pattern to work. + +How can I add default values to a column? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine does not support to set the default values in columns through the "DEFAULT" keyword in SQL. +This is not necessary however, you can just use your class properties as default values. These are then used +upon insert: + +.. code-block:: php + + class User + { + const STATUS_DISABLED = 0; + const STATUS_ENABLED = 1; + + private $algorithm = "sha1"; + private $status = self:STATUS_DISABLED; + } + +. + +Mapping +------- + +Why do I get exceptions about unique constraint failures during ``$em->flush()``? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine does not check if you are re-adding entities with a primary key that already exists +or adding entities to a collection twice. You have to check for both conditions yourself +in the code before calling ``$em->flush()`` if you know that unique constraint failures +can occur. + +In `Symfony2 `_ for example there is a Unique Entity Validator +to achieve this task. + +For collections you can check with ``$collection->contains($entity)`` if an entity is already +part of this collection. For a FETCH=LAZY collection this will initialize the collection, +however for FETCH=EXTRA_LAZY this method will use SQL to determine if this entity is already +part of the collection. + +Associations +------------ + +What is wrong when I get an InvalidArgumentException "A new entity was found through the relationship.."? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This exception is thrown during ``EntityManager#flush()`` when there exists an object in the identity map +that contains a reference to an object that Doctrine does not know about. Say for example you grab +a "User"-entity from the database with a specific id and set a completely new object into one of the associations +of the User object. If you then call ``EntityManager#flush()`` without letting Doctrine know about +this new object using ``EntityManager#persist($newObject)`` you will see this exception. + +You can solve this exception by: + +* Calling ``EntityManager#persist($newObject)`` on the new object +* Using cascade=persist on the association that contains the new object + +How can I filter an association? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Natively you can't filter associations in 2.0 and 2.1. You should use DQL queries to query for the filtered set of entities. + +I call clear() on a One-To-Many collection but the entities are not deleted +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is an expected behavior that has to do with the inverse/owning side handling of Doctrine. +By definition a One-To-Many association is on the inverse side, that means changes to it +will not be recognized by Doctrine. + +If you want to perform the equivalent of the clear operation you have to iterate the +collection and set the owning side many-to-one reference to NULL as well to detach all entities +from the collection. This will trigger the appropriate UPDATE statements on the database. + +How can I add columns to a many-to-many table? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The many-to-many association is only supporting foreign keys in the table definition +To work with many-to-many tables containing extra columns you have to use the +foreign keys as primary keys feature of Doctrine introduced in version 2.1. + +See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`. + + +How can i paginate fetch-joined collections? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are issuing a DQL statement that fetches a collection as well you cannot easily iterate +over this collection using a LIMIT statement (or vendor equivalent). + +Doctrine does not offer a solution for this out of the box but there are several extensions +that do: + +* `DoctrineExtensions `_ +* `Pagerfanta `_ + +Why does pagination not work correctly with fetch joins? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pagination in Doctrine uses a LIMIT clause (or vendor equivalent) to restrict the results. +However when fetch-joining this is not returning the correct number of results since joining +with a one-to-many or many-to-many association multiplies the number of rows by the number +of associated entities. + +See the previous question for a solution to this task. + +Inheritance +----------- + +Can I use Inheritance with Doctrine 2? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2. + +See the documentation chapter on :doc:`inheritance mapping ` for +the details. + +Why does Doctrine not create proxy objects for my inheritance hierarchy? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you set a many-to-one or one-to-one association target-entity to any parent class of +an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of. +To find this out it has to execute a SQL query to look this information up in the database. + +EntityGenerator +--------------- + +Why does the EntityGenerator not do X? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The EntityGenerator is not a full fledged code-generator that solves all tasks. Code-Generation +is not a first-class priority in Doctrine 2 anymore (compared to Doctrine 1). The EntityGenerator +is supposed to kick-start you, but not towards 100%. + +Why does the EntityGenerator not generate inheritance correctly? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy. +This is why the generation of inherited entities does not fully work. You have to adjust some additional +code to get this one working correctly. + +Performance +----------- + +Why is an extra SQL query executed every time I fetch an entity with a one-to-one relation? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If Doctrine detects that you are fetching an inverse side one-to-one association +it has to execute an additional query to load this object, because it cannot know +if there is no such object (setting null) or if it should set a proxy and which id this proxy has. + +To solve this problem currently a query has to be executed to find out this information. + +Doctrine Query Language +----------------------- + +What is DQL? +~~~~~~~~~~~~ + +DQL stands for Doctrine Query Language, a query language that very much looks like SQL +but has some important benefits when using Doctrine: + +- It uses class names and fields instead of tables and columns, separating concerns between backend and your object model. +- It utilizes the metadata defined to offer a range of shortcuts when writing. For example you do not have to specify the ON clause of joins, since Doctrine already knows about them. +- It adds some functionality that is related to object management and transforms them into SQL. + +It also has some drawbacks of course: + +- The syntax is slightly different to SQL so you have to learn and remember the differences. +- To be vendor independent it can only implement a subset of all the existing SQL dialects. Vendor specific functionality and optimizations cannot be used through DQL unless implemented by you explicitly. +- For some DQL constructs subselects are used which are known to be slow in MySQL. + +Can I sort by a function (for example ORDER BY RAND()) in DQL? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +No, it is not supported to sort by function in DQL. If you need this functionality you should either +use a native-query or come up with another solution. As a side note: Sorting with ORDER BY RAND() is painfully slow +starting with 1000 rows. + +A Query fails, how can I debug it? +---------------------------------- + +First, if you are using the QueryBuilder you can use +``$queryBuilder->getDQL()`` to get the DQL string of this query. The +corresponding SQL you can get from the Query instance by calling +``$query->getSQL()``. + +.. code-block:: php + + createQuery($dql); + var_dump($query->getSQL()); + + $qb = $entityManager->createQueryBuilder(); + $qb->select('u')->from('User', 'u'); + var_dump($qb->getDQL()); diff --git a/vendor/doctrine/orm/docs/en/reference/filters.rst b/vendor/doctrine/orm/docs/en/reference/filters.rst new file mode 100644 index 00000000000..a5c0ee4cf14 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/filters.rst @@ -0,0 +1,93 @@ +Filters +======= + +.. versionadded:: 2.2 + +Doctrine 2.2 features a filter system that allows the developer to add SQL to +the conditional clauses of queries, regardless the place where the SQL is +generated (e.g. from a DQL query, or by loading associated entities). + +The filter functionality works on SQL level. Whether a SQL query is generated +in a Persister, during lazy loading, in extra lazy collections or from DQL. +Each time the system iterates over all the enabled filters, adding a new SQL +part as a filter returns. + +By adding SQL to the conditional clauses of queries, the filter system filters +out rows belonging to the entities at the level of the SQL result set. This +means that the filtered entities are never hydrated (which can be expensive). + + +Example filter class +-------------------- +Throughout this document the example ``MyLocaleFilter`` class will be used to +illustrate how the filter feature works. A filter class must extend the base +``Doctrine\ORM\Query\Filter\SQLFilter`` class and implement the ``addFilterConstraint`` +method. The method receives the ``ClassMetadata`` of the filtered entity and the +table alias of the SQL table of the entity. + +.. note:: + + In the case of joined or single table inheritance, you always get passed the ClassMetadata of the + inheritance root. This is necessary to avoid edge cases that would break the SQL when applying the filters. + +Parameters for the query should be set on the filter object by +``SQLFilter#setParameter()``. Only parameters set via this function can be used +in filters. The ``SQLFilter#getParameter()`` function takes care of the +proper quoting of parameters. + +.. code-block:: php + + reflClass->implementsInterface('LocaleAware')) { + return ""; + } + + return $targetTableAlias.'.locale = ' . $this->getParameter('locale'); // getParameter applies quoting automatically + } + } + + +Configuration +------------- +Filter classes are added to the configuration as following: + +.. code-block:: php + + addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter"); + + +The ``Configuration#addFilter()`` method takes a name for the filter and the name of the +class responsible for the actual filtering. + + +Disabling/Enabling Filters and Setting Parameters +--------------------------------------------------- +Filters can be disabled and enabled via the ``FilterCollection`` which is +stored in the ``EntityManager``. The ``FilterCollection#enable($name)`` method +will retrieve the filter object. You can set the filter parameters on that +object. + +.. code-block:: php + + getFilters()->enable("locale"); + $filter->setParameter('locale', 'en'); + + // Disable it + $filter = $em->getFilters()->disable("locale"); + +.. warning:: + Disabling and enabling filters has no effect on managed entities. If you + want to refresh or reload an object after having modified a filter or the + FilterCollection, then you should clear the EntityManager and re-fetch your + entities, having the new rules for filtering applied. diff --git a/vendor/doctrine/orm/docs/en/reference/improving-performance.rst b/vendor/doctrine/orm/docs/en/reference/improving-performance.rst new file mode 100644 index 00000000000..d9de6c80863 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/improving-performance.rst @@ -0,0 +1,68 @@ +Improving Performance +===================== + +Bytecode Cache +-------------- + +It is highly recommended to make use of a bytecode cache like APC. +A bytecode cache removes the need for parsing PHP code on every +request and can greatly improve performance. + + "If you care about performance and don't use a bytecode + cache then you don't really care about performance. Please get one + and start using it." + + *Stas Malyshev, Core Contributor to PHP and Zend Employee* + + +Metadata and Query caches +------------------------- + +As already mentioned earlier in the chapter about configuring +Doctrine, it is strongly discouraged to use Doctrine without a +Metadata and Query cache (preferably with APC or Memcache as the +cache driver). Operating Doctrine without these caches means +Doctrine will need to load your mapping information on every single +request and has to parse each DQL query on every single request. +This is a waste of resources. + +Alternative Query Result Formats +-------------------------------- + +Make effective use of the available alternative query result +formats like nested array graphs or pure scalar results, especially +in scenarios where data is loaded for read-only purposes. + +Read-Only Entities +------------------ + +Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping +references for details). This means that the entity marked as read only is never considered +for updates, which means when you call flush on the EntityManager these entities are skipped +even if properties changed. Read-Only allows to persist new entities of a kind and remove existing +ones, they are just not considered for updates. + +Extra-Lazy Collections +---------------------- + +If entities hold references to large collections you will get performance and memory problems initializing them. +To solve this issue you can use the EXTRA_LAZY fetch-mode feature for collections. See the :doc:`tutorial <../tutorials/extra-lazy-associations>` +for more information on how this fetch mode works. + +Temporarily change fetch mode in DQL +------------------------------------ + +See :ref:`Doctrine Query Language chapter ` + + +Apply Best Practices +-------------------- + +A lot of the points mentioned in the Best Practices chapter will +also positively affect the performance of Doctrine. + + +Change Tracking policies +------------------------ + +See: :doc:`Change Tracking Policies ` diff --git a/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst b/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst new file mode 100644 index 00000000000..9a0d3c208d3 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst @@ -0,0 +1,605 @@ +Inheritance Mapping +=================== + +Mapped Superclasses +------------------- + +A mapped superclass is an abstract or concrete class that provides +persistent entity state and mapping information for its subclasses, +but which is not itself an entity. Typically, the purpose of such a +mapped superclass is to define state and mapping information that +is common to multiple entity classes. + +Mapped superclasses, just as regular, non-mapped classes, can +appear in the middle of an otherwise mapped inheritance hierarchy +(through Single Table Inheritance or Class Table Inheritance). + +.. note:: + + A mapped superclass cannot be an entity, it is not query-able and + persistent relationships defined by a mapped superclass must be + unidirectional (with an owning side only). This means that One-To-Many + associations are not possible on a mapped superclass at all. + Furthermore Many-To-Many associations are only possible if the + mapped superclass is only used in exactly one entity at the moment. + For further support of inheritance, the single or + joined table inheritance features have to be used. + + +Example: + +.. code-block:: php + + `_ +is an inheritance mapping strategy where all classes of a hierarchy +are mapped to a single database table. In order to distinguish +which row represents which type in the hierarchy a so-called +discriminator column is used. + +Example: + +.. configuration-block:: + + .. code-block:: php + + `_ +is an inheritance mapping strategy where each class in a hierarchy +is mapped to several tables: its own table and the tables of all +parent classes. The table of a child class is linked to the table +of a parent class through a foreign key constraint. Doctrine 2 +implements this strategy through the use of a discriminator column +in the topmost table of the hierarchy because this is the easiest +way to achieve polymorphic queries with Class Table Inheritance. + +Example: + +.. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # user mapping + MyProject\Model\User: + type: mappedSuperclass + # other fields mapping + manyToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + cascade: [ persist, merge ] + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + cascade: [ persist, merge, detach ] + + # admin mapping + MyProject\Model\Admin: + type: entity + associationOverride: + address: + joinColumn: + adminaddress_id: + name: adminaddress_id + referencedColumnName: id + groups: + joinTable: + name: users_admingroups + joinColumns: + adminuser_id: + referencedColumnName: id + inverseJoinColumns: + admingroup_id: + referencedColumnName: id + + +Things to note: + +- The "association override" specifies the overrides base on the property name. +- This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany) +- The association type *CANNOT* be changed. +- The override could redefine the joinTables or joinColumns depending on the association type. + +Attribute Override +~~~~~~~~~~~~~~~~~~~~ +Override the mapping of a field. + +Could be used by an entity that extends a mapped superclass to override a field mapping defined by the mapped superclass. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # user mapping + MyProject\Model\User: + type: mappedSuperclass + id: + id: + type: integer + column: user_id + length: 150 + generator: + strategy: AUTO + fields: + name: + type: string + column: user_name + length: 250 + nullable: true + unique: false + #other fields mapping + + + # guest mapping + MyProject\Model\Guest: + type: entity + attributeOverride: + id: + column: guest_id + type: integer + length: 140 + name: + column: guest_name + type: string + length: 240 + nullable: false + unique: true + +Things to note: + +- The "attribute override" specifies the overrides base on the property name. +- The column type *CANNOT* be changed. If the column type is not equal you get a ``MappingException`` +- The override can redefine all the columns except the type. + +Query the Type +-------------- + +It may happen that the entities of a special type should be queried. Because there +is no direct access to the discriminator column, Doctrine provides the +``INSTANCE OF`` construct. + +The following example shows how to use ``INSTANCE OF``. There is a three level hierarchy +with a base entity ``NaturalPerson`` which is extended by ``Staff`` which in turn +is extended by ``Technician``. + +Querying for the staffs without getting any technicians can be achieved by this DQL: + +.. code-block:: php + + createQuery("SELECT staff FROM MyProject\Model\Staff staff WHERE staff NOT INSTANCE OF MyProject\Model\Technician"); + $staffs = $query->getResult(); diff --git a/vendor/doctrine/orm/docs/en/reference/installation.rst b/vendor/doctrine/orm/docs/en/reference/installation.rst new file mode 100644 index 00000000000..8b732ad6058 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/installation.rst @@ -0,0 +1,5 @@ +Installation +============ + +The installation chapter has moved to `Installation and Configuration +`_. diff --git a/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst b/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst new file mode 100644 index 00000000000..49976f12ba6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst @@ -0,0 +1,184 @@ +Limitations and Known Issues +============================ + +We try to make using Doctrine2 a very pleasant experience. +Therefore we think it is very important to be honest about the +current limitations to our users. Much like every other piece of +software Doctrine2 is not perfect and far from feature complete. +This section should give you an overview of current limitations of +Doctrine 2 as well as critical known issues that you should know +about. + +Current Limitations +------------------- + +There is a set of limitations that exist currently which might be +solved in the future. Any of this limitations now stated has at +least one ticket in the Tracker and is discussed for future +releases. + +Join-Columns with non-primary keys +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is not possible to use join columns pointing to non-primary keys. Doctrine will think these are the primary +keys and create lazy-loading proxies with the data, which can lead to unexpected results. Doctrine can for performance +reasons not validate the correctness of this settings at runtime but only through the Validate Schema command. + +Mapping Arrays to a Join Table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Related to the previous limitation with "Foreign Keys as +Identifier" you might be interested in mapping the same table +structure as given above to an array. However this is not yet +possible either. See the following example: + +.. code-block:: sql + + CREATE TABLE product ( + id INTEGER, + name VARCHAR, + PRIMARY KEY(id) + ); + + CREATE TABLE product_attributes ( + product_id INTEGER, + attribute_name VARCHAR, + attribute_value VARCHAR, + PRIMARY KEY (product_id, attribute_name) + ); + +This schema should be mapped to a Product Entity as follows: + +.. code-block:: php + + class Product + { + private $id; + private $name; + private $attributes = array(); + } + +Where the ``attribute_name`` column contains the key and +``attribute_value`` contains the value of each array element in +``$attributes``. + +The feature request for persistence of primitive value arrays +`is described in the DDC-298 ticket `_. + +Cascade Merge with Bi-directional Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two bugs now that concern the use of cascade merge in combination with bi-directional associations. +Make sure to study the behavior of cascade merge if you are using it: + +- `DDC-875 `_ Merge can sometimes add the same entity twice into a collection +- `DDC-763 `_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability" + +Custom Persisters +~~~~~~~~~~~~~~~~~ + +A Persister in Doctrine is an object that is responsible for the +hydration and write operations of an entity against the database. +Currently there is no way to overwrite the persister implementation +for a given entity, however there are several use-cases that can +benefit from custom persister implementations: + + +- `Add Upsert Support `_ +- `Evaluate possible ways in which stored-procedures can be used `_ +- The previous Filter Rules Feature Request + +Persist Keys of Collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PHP Arrays are ordered hash-maps and so should be the +``Doctrine\Common\Collections\Collection`` interface. We plan to +evaluate a feature that optionally persists and hydrates the keys +of a Collection instance. + +`Ticket DDC-213 `_ + +Mapping many tables to one entity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is not possible to map several equally looking tables onto one +entity. For example if you have a production and an archive table +of a certain business concept then you cannot have both tables map +to the same entity. + +Behaviors +~~~~~~~~~ + +Doctrine 2 will **never** include a behavior system like Doctrine 1 +in the core library. We don't think behaviors add more value than +they cost pain and debugging hell. Please see the many different +blog posts we have written on this topics: + +- `Doctrine2 "Behaviors" in a Nutshell `_ +- `A re-usable Versionable behavior for Doctrine2 `_ +- `Write your own ORM on top of Doctrine2 `_ +- `Doctrine 2 Behavioral Extensions `_ +- `Doctrator _ + +Doctrine 2 has enough hooks and extension points so that **you** can +add whatever you want on top of it. None of this will ever become +core functionality of Doctrine2 however, you will have to rely on +third party extensions for magical behaviors. + +Nested Set +~~~~~~~~~~ + +NestedSet was offered as a behavior in Doctrine 1 and will not be +included in the core of Doctrine 2. However there are already two +extensions out there that offer support for Nested Set with +Doctrine 2: + + +- `Doctrine2 Hierarchical-Structural Behavior `_ +- `Doctrine2 NestedSet `_ + +Known Issues +------------ + +The Known Issues section describes critical/blocker bugs and other +issues that are either complicated to fix, not fixable due to +backwards compatibility issues or where no simple fix exists (yet). +We don't plan to add every bug in the tracker there, just those +issues that can potentially cause nightmares or pain of any sort. + +See the Open Bugs on Jira for more details on `bugs, improvement and feature +requests +`_. + +Identifier Quoting and Legacy Databases +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For compatibility reasons between all the supported vendors and +edge case problems Doctrine 2 does **NOT** do automatic identifier +quoting. This can lead to problems when trying to get +legacy-databases to work with Doctrine 2. + + +- You can quote column-names as described in the + :doc:`Basic-Mapping ` section. +- You cannot quote join column names. +- You cannot use non [a-zA-Z0-9\_]+ characters, they will break + several SQL statements. + +Having problems with these kind of column names? Many databases +support all CRUD operations on views that semantically map to +certain tables. You can create views for all your problematic +tables and column names to avoid the legacy quoting nightmare. + +Microsoft SQL Server and Doctrine "datetime" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine assumes that you use ``DateTime2`` data-types. If your legacy database contains DateTime +datatypes then you have to add your own data-type (see Basic Mapping for an example). + +MySQL with MyISAM tables +~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine cannot provide atomic operations when calling ``EntityManager#flush()`` if one +of the tables involved uses the storage engine MyISAM. You must use InnoDB or +other storage engines that support transactions if you need integrity. diff --git a/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst b/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst new file mode 100644 index 00000000000..6b9cb31e42c --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst @@ -0,0 +1,194 @@ +Metadata Drivers +================ + +The heart of an object relational mapper is the mapping information +that glues everything together. It instructs the EntityManager how +it should behave when dealing with the different entities. + +Core Metadata Drivers +--------------------- + +Doctrine provides a few different ways for you to specify your +metadata: + + +- **XML files** (XmlDriver) +- **Class DocBlock Annotations** (AnnotationDriver) +- **YAML files** (YamlDriver) +- **PHP Code in files or static functions** (PhpDriver) + +Something important to note about the above drivers is they are all +an intermediate step to the same end result. The mapping +information is populated to ``Doctrine\ORM\Mapping\ClassMetadata`` +instances. So in the end, Doctrine only ever has to work with the +API of the ``ClassMetadata`` class to get mapping information for +an entity. + +.. note:: + + The populated ``ClassMetadata`` instances are also cached + so in a production environment the parsing and populating only ever + happens once. You can configure the metadata cache implementation + using the ``setMetadataCacheImpl()`` method on the + ``Doctrine\ORM\Configuration`` class: + + .. code-block:: php + + getConfiguration()->setMetadataCacheImpl(new ApcCache()); + + +If you want to use one of the included core metadata drivers you +just need to configure it. All the drivers are in the +``Doctrine\ORM\Mapping\Driver`` namespace: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Implementing Metadata Drivers +----------------------------- + +In addition to the included metadata drivers you can very easily +implement your own. All you need to do is define a class which +implements the ``Driver`` interface: + +.. code-block:: php + + _loadMappingFile($file); + + // populate ClassMetadataInfo instance from $data + } + + /** + * {@inheritdoc} + */ + protected function _loadMappingFile($file) + { + // parse contents of $file and return php data structure + } + } + +.. note:: + + When using the ``AbstractFileDriver`` it requires that you + only have one entity defined per file and the file named after the + class described inside where namespace separators are replaced by + periods. So if you have an entity named ``Entities\User`` and you + wanted to write a mapping file for your driver above you would need + to name the file ``Entities.User.dcm.ext`` for it to be + recognized. + + +Now you can use your ``MyMetadataDriver`` implementation by setting +it with the ``setMetadataDriverImpl()`` method: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +ClassMetadata +------------- + +The last piece you need to know and understand about metadata in +Doctrine 2 is the API of the ``ClassMetadata`` classes. You need to +be familiar with them in order to implement your own drivers but +more importantly to retrieve mapping information for a certain +entity when needed. + +You have all the methods you need to manually specify the mapping +information instead of using some mapping file to populate it from. +The base ``ClassMetadataInfo`` class is responsible for only data +storage and is not meant for runtime use. It does not require that +the class actually exists yet so it is useful for describing some +entity before it exists and using that information to generate for +example the entities themselves. The class ``ClassMetadata`` +extends ``ClassMetadataInfo`` and adds some functionality required +for runtime usage and requires that the PHP class is present and +can be autoloaded. + +You can read more about the API of the ``ClassMetadata`` classes in +the PHP Mapping chapter. + +Getting ClassMetadata Instances +------------------------------- + +If you want to get the ``ClassMetadata`` instance for an entity in +your project to programmatically use some mapping information to +generate some HTML or something similar you can retrieve it through +the ``ClassMetadataFactory``: + +.. code-block:: php + + getMetadataFactory(); + $class = $cmf->getMetadataFor('MyEntityName'); + +Now you can learn about the entity and use the data stored in the +``ClassMetadata`` instance to get all mapped fields for example and +iterate over them: + +.. code-block:: php + + fieldMappings as $fieldMapping) { + echo $fieldMapping['fieldName'] . "\n"; + } + + diff --git a/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst b/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst new file mode 100644 index 00000000000..9d82d8d8b7d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst @@ -0,0 +1,150 @@ +Implementing a NamingStrategy +============================== + +.. versionadded:: 2.3 + +Using a naming strategy you can provide rules for automatically generating +database identifiers, columns and tables names +when the table/column name is not given. +This feature helps reduce the verbosity of the mapping document, +eliminating repetitive noise (eg: ``TABLE_``). + + +Configuring a naming strategy +----------------------------- +The default strategy used by Doctrine is quite minimal. + +By default the ``Doctrine\ORM\Mapping\DefaultNamingStrategy`` +uses the simple class name and the attributes names to generate tables and columns + +You can specify a different strategy by calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` : + +.. code-block:: php + + setNamingStrategy($namingStrategy); + +Underscore naming strategy +--------------------------- + +``\Doctrine\ORM\Mapping\UnderscoreNamingStrategy`` is a built-in strategy +that might be a useful if you want to use a underlying convention. + +.. code-block:: php + + setNamingStrategy($namingStrategy); + +Then SomeEntityName will generate the table SOME_ENTITY_NAME when CASE_UPPER +or some_entity_name using CASE_LOWER is given. + + +Naming strategy interface +------------------------- +The interface ``Doctrine\ORM\Mapping\NamingStrategy`` allows you to specify +a "naming standard" for database tables and columns. + +.. code-block:: php + + referenceColumnName(); + } + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return strtolower($this->classToTableName($sourceEntity) . '_' . + $this->classToTableName($targetEntity)); + } + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return strtolower($this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName())); + } + } + +Configuring the namingstrategy is easy if. +Just set your naming strategy calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` :. + +.. code-block:: php + + setNamingStrategy($namingStrategy); diff --git a/vendor/doctrine/orm/docs/en/reference/native-sql.rst b/vendor/doctrine/orm/docs/en/reference/native-sql.rst new file mode 100644 index 00000000000..75231b7a99b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/native-sql.rst @@ -0,0 +1,905 @@ +Native SQL +========== + +With ``NativeQuery`` you can execute native SELECT SQL statements +and map the results to Doctrine entities or any other result format +supported by Doctrine. + +In order to make this mapping possible, you need to describe +to Doctrine what columns in the result map to which entity property. +This description is represented by a ``ResultSetMapping`` object. + +With this feature you can map arbitrary SQL code to objects, such as highly +vendor-optimized SQL or stored-procedures. + +Writing ``ResultSetMapping`` from scratch is complex, but there is a convenience +wrapper around it called a ``ResultSetMappingBuilder``. It can generate +the mappings for you based on Entities and even generates the ``SELECT`` +clause based on this information for you. + +.. note:: + + If you want to execute DELETE, UPDATE or INSERT statements + the Native SQL API cannot be used and will probably throw errors. + Use ``EntityManager#getConnection()`` to access the native database + connection and call the ``executeUpdate()`` method for these + queries. + +The NativeQuery class +--------------------- + +To create a ``NativeQuery`` you use the method +``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As you can see in +the signature of this method, it expects 2 ingredients: The SQL you want to +execute and the ``ResultSetMapping`` that describes how the results will be +mapped. + +Once you obtained an instance of a ``NativeQuery``, you can bind parameters to +it with the same API that ``Query`` has and execute it. + +.. code-block:: php + + createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +ResultSetMappingBuilder +----------------------- + +An easy start into ResultSet mapping is the ``ResultSetMappingBuilder`` object. +This has several benefits: + +- The builder takes care of automatically updating your ``ResultSetMapping`` + when the fields or associations change on the metadata of an entity. +- You can generate the required ``SELECT`` expression for a builder + by converting it to a string. +- The API is much simpler than the usual ``ResultSetMapping`` API. + +One downside is that the builder API does not yet support entities +with inheritance hierachies. + +.. code-block:: php + + addRootEntityFromClassMetadata('MyProject\User', 'u'); + $rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', 'u', 'address', array('id' => 'address_id')); + +The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well. + +.. versionadded:: 2.4 + +Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause +from a ``ResultSetMappingBuilder``. You can either cast the builder +object to ``(string)`` and the DQL aliases are used as SQL table aliases +or use the ``generateSelectClause($tableAliases)`` method and pass +a mapping from DQL alias (key) to SQL alias (value) + +.. code-block:: php + + generateSelectClause(array( + 'u' => 't1', + 'g' => 't2' + )); + $sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id"; + + +The ResultSetMapping +-------------------- + +Understanding the ``ResultSetMapping`` is the key to using a +``NativeQuery``. A Doctrine result can contain the following +components: + + +- Entity results. These represent root result elements. +- Joined entity results. These represent joined entities in + associations of root entity results. +- Field results. These represent a column in the result set that + maps to a field of an entity. A field result always belongs to an + entity result or joined entity result. +- Scalar results. These represent scalar values in the result set + that will appear in each result row. Adding scalar results to a + ResultSetMapping can also cause the overall result to become + **mixed** (see DQL - Doctrine Query Language) if the same + ResultSetMapping also contains entity results. +- Meta results. These represent columns that contain + meta-information, such as foreign keys and discriminator columns. + When querying for objects (``getResult()``), all meta columns of + root entities or joined entities must be present in the SQL query + and mapped accordingly using ``ResultSetMapping#addMetaResult``. + +.. note:: + + It might not surprise you that Doctrine uses + ``ResultSetMapping`` internally when you create DQL queries. As + the query gets parsed and transformed to SQL, Doctrine fills a + ``ResultSetMapping`` that describes how the results should be + processed by the hydration routines. + + +We will now look at each of the result types that can appear in a +ResultSetMapping in detail. + +Entity results +~~~~~~~~~~~~~~ + +An entity result describes an entity type that appears as a root +element in the transformed result. You add an entity result through +``ResultSetMapping#addEntityResult()``. Let's take a look at the +method signature in detail: + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + + $query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +The result would look like this: + +.. code-block:: php + + array( + [0] => User (Object) + ) + +Note that this would be a partial object if the entity has more +fields than just id and name. In the example above the column and +field names are identical but that is not necessary, of course. +Also note that the query string passed to createNativeQuery is +**real native SQL**. Doctrine does not touch this SQL in any way. + +In the previous basic example, a User had no relations and the +table the class is mapped to owns no foreign keys. The next example +assumes User has a unidirectional or bidirectional one-to-one +association to a CmsAddress, where the User is the owning side and +thus owns the foreign key. + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addMetaResult('u', 'address_id', 'address_id'); + + $query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +Foreign keys are used by Doctrine for lazy-loading purposes when +querying for objects. In the previous example, each user object in +the result will have a proxy (a "ghost") in place of the address +that contains the address\_id. When the ghost proxy is accessed, it +loads itself based on this key. + +Consequently, associations that are *fetch-joined* do not require +the foreign keys to be present in the SQL result set, only +associations that are lazy. + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addJoinedEntityResult('Address' , 'a', 'u', 'address'); + $rsm->addFieldResult('a', 'address_id', 'id'); + $rsm->addFieldResult('a', 'street', 'street'); + $rsm->addFieldResult('a', 'city', 'city'); + + $sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' . + 'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?'; + $query = $this->_em->createNativeQuery($sql, $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +In this case the nested entity ``Address`` is registered with the +``ResultSetMapping#addJoinedEntityResult`` method, which notifies +Doctrine that this entity is not hydrated at the root level, but as +a joined entity somewhere inside the object graph. In this case we +specify the alias 'u' as third parameter and ``address`` as fourth +parameter, which means the ``Address`` is hydrated into the +``User::$address`` property. + +If a fetched entity is part of a mapped hierarchy that requires a +discriminator column, this column must be present in the result set +as a meta column so that Doctrine can create the appropriate +concrete type. This is shown in the following example where we +assume that there are one or more subclasses that extend User and +either Class Table Inheritance or Single Table Inheritance is used +to map the hierarchy (both use a discriminator column). + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column + $rsm->setDiscriminatorColumn('u', 'discr'); + + $query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +Note that in the case of Class Table Inheritance, an example as +above would result in partial objects if any objects in the result +are actually a subtype of User. When using DQL, Doctrine +automatically includes the necessary joins for this mapping +strategy but with native SQL it is your responsibility. + +Named Native Query +------------------ + +You can also map a native query using a named native query mapping. + +To achieve that, you must describe the SQL resultset structure +using named native query (and sql resultset mappings if is a several resultset mappings). + +Like named query, a named native query can be defined at class level or in a XML or YAML file. + + +A resultSetMapping parameter is defined in @NamedNativeQuery, +it represents the name of a defined @SqlResultSetMapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\User: + type: entity + namedNativeQueries: + fetchMultipleJoinsEntityResults: + name: fetchMultipleJoinsEntityResults + resultSetMapping: mappingMultipleJoinsEntityResults + query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + sqlResultSetMappings: + mappingMultipleJoinsEntityResults: + name: mappingMultipleJoinsEntityResults + columnResult: + 0: + name: numphones + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + column: u_id + 1: + name: name + column: u_name + 2: + name: status + column: u_status + 1: + entityClass: Address + fieldResult: + 0: + name: id + column: a_id + 1: + name: zip + column: a_zip + 2: + name: country + column: a_country + + +Things to note: + - The resultset mapping declares the entities retrieved by this native query. + - Each field of the entity is bound to a SQL alias (or column name). + - All fields of the entity including the ones of subclasses + and the foreign key columns of related entities have to be present in the SQL query. + - Field definitions are optional provided that they map to the same + column name as the one declared on the class property. + - ``__CLASS__`` is an alias for the mapped class + + +In the above example, +the ``fetchJoinedAddress`` named query use the joinMapping result set mapping. +This mapping returns 2 entities, User and Address, each property is declared and associated to a column name, +actually the column name retrieved by the query. + +Let's now see an implicit declaration of the property / column. + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT * FROM addresses + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + findAll: + resultSetMapping: mappingFindAll + query: SELECT * FROM addresses + sqlResultSetMappings: + mappingFindAll: + name: mappingFindAll + entityResult: + address: + entityClass: Address + + +In this example, we only describe the entity member of the result set mapping. +The property / column mappings is done using the entity mapping values. +In this case the model property is bound to the model_txt column. +If the association to a related entity involve a composite primary key, +a @FieldResult element should be used for each foreign key column. +The @FieldResult name is composed of the property name for the relationship, +followed by a dot ("."), followed by the name or the field or property of the primary key. + + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\User: + type: entity + namedNativeQueries: + fetchJoinedAddress: + name: fetchJoinedAddress + resultSetMapping: mappingJoinedAddress + query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? + sqlResultSetMappings: + mappingJoinedAddress: + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + 1: + name: name + 2: + name: status + 3: + name: address.id + column: a_id + 4: + name: address.zip + column: a_zip + 5: + name: address.city + column: a_city + 6: + name: address.country + column: a_country + + + +If you retrieve a single entity and if you use the default mapping, +you can use the resultClass attribute instead of resultSetMapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT * FROM addresses WHERE id = ? + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + findAll: + name: findAll + resultClass: Address + query: SELECT * FROM addresses + + +In some of your native queries, you'll have to return scalar values, +for example when building report queries. +You can map them in the @SqlResultsetMapping through @ColumnResult. +You actually can even mix, entities and scalar returns in the same native query (this is probably not that common though). + +.. configuration-block:: + + .. code-block:: php + + + + + SELECT COUNT(*) AS count FROM addresses + + + + + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + count: + name: count + resultSetMapping: mappingCount + query: SELECT COUNT(*) AS count FROM addresses + sqlResultSetMappings: + mappingCount: + name: mappingCount + columnResult: + count: + name: count diff --git a/vendor/doctrine/orm/docs/en/reference/partial-objects.rst b/vendor/doctrine/orm/docs/en/reference/partial-objects.rst new file mode 100644 index 00000000000..396eecce3d0 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/partial-objects.rst @@ -0,0 +1,90 @@ +Partial Objects +=============== + +A partial object is an object whose state is not fully initialized +after being reconstituted from the database and that is +disconnected from the rest of its data. The following section will +describe why partial objects are problematic and what the approach +of Doctrine2 to this problem is. + +.. note:: + + The partial object problem in general does not apply to + methods or queries where you do not retrieve the query result as + objects. Examples are: ``Query#getArrayResult()``, + ``Query#getScalarResult()``, ``Query#getSingleScalarResult()``, + etc. + +.. warning:: + + Use of partial objects is tricky. Fields that are not retrieved + from the database will not be updated by the UnitOfWork even if they + get changed in your objects. You can only promote a partial object + to a fully-loaded object by calling ``EntityManager#refresh()`` + or a DQL query with the refresh flag. + + +What is the problem? +-------------------- + +In short, partial objects are problematic because they are usually +objects with broken invariants. As such, code that uses these +partial objects tends to be very fragile and either needs to "know" +which fields or methods can be safely accessed or add checks around +every field access or method invocation. The same holds true for +the internals, i.e. the method implementations, of such objects. +You usually simply assume the state you need in the method is +available, after all you properly constructed this object before +you pushed it into the database, right? These blind assumptions can +quickly lead to null reference errors when working with such +partial objects. + +It gets worse with the scenario of an optional association (0..1 to +1). When the associated field is NULL, you don't know whether this +object does not have an associated object or whether it was simply +not loaded when the owning object was loaded from the database. + +These are reasons why many ORMs do not allow partial objects at all +and instead you always have to load an object with all its fields +(associations being proxied). One secure way to allow partial +objects is if the programming language/platform allows the ORM tool +to hook deeply into the object and instrument it in such a way that +individual fields (not only associations) can be loaded lazily on +first access. This is possible in Java, for example, through +bytecode instrumentation. In PHP though this is not possible, so +there is no way to have "secure" partial objects in an ORM with +transparent persistence. + +Doctrine, by default, does not allow partial objects. That means, +any query that only selects partial object data and wants to +retrieve the result as objects (i.e. ``Query#getResult()``) will +raise an exception telling you that partial objects are dangerous. +If you want to force a query to return you partial objects, +possibly as a performance tweak, you can use the ``partial`` +keyword as follows: + +.. code-block:: php + + createQuery("select partial u.{id,name} from MyApp\Domain\User u"); + +You can also get a partial reference instead of a proxy reference by +calling: + +.. code-block:: php + + getPartialReference('MyApp\Domain\User', 1); + +Partial references are objects with only the identifiers set as they +are passed to the second argument of the ``getPartialReference()`` method. +All other fields are null. + +When should I force partial objects? +------------------------------------ + +Mainly for optimization purposes, but be careful of premature +optimization as partial objects lead to potentially more fragile +code. + + diff --git a/vendor/doctrine/orm/docs/en/reference/php-mapping.rst b/vendor/doctrine/orm/docs/en/reference/php-mapping.rst new file mode 100644 index 00000000000..78a721411d6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/php-mapping.rst @@ -0,0 +1,325 @@ +PHP Mapping +=========== + +Doctrine 2 also allows you to provide the ORM metadata in the form +of plain PHP code using the ``ClassMetadata`` API. You can write +the code in PHP files or inside of a static function named +``loadMetadata($class)`` on the entity class itself. + +PHP Files +--------- + +If you wish to write your mapping information inside PHP files that +are named after the entity and included to populate the metadata +for an entity you can do so by using the ``PHPDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Now imagine we had an entity named ``Entities\User`` and we wanted +to write a mapping file for it using the above configured +``PHPDriver`` instance: + +.. code-block:: php + + mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string', + 'options' => array( + 'fixed' => true, + 'comment' => "User's login name" + ) + )); + + $metadata->mapField(array( + 'fieldName' => 'login_count', + 'type' => 'integer', + 'nullable' => false, + 'options' => array( + 'unsigned' => true, + 'default' => 0 + ) + )); + +Now we can easily retrieve the populated ``ClassMetadata`` instance +where the ``PHPDriver`` includes the file and the +``ClassMetadataFactory`` caches it for later retrieval: + +.. code-block:: php + + getClassMetadata('Entities\User'); + // or + $class = $em->getMetadataFactory()->getMetadataFor('Entities\User'); + +Static Function +--------------- + +In addition to the PHP files you can also specify your mapping +information inside of a static function defined on the entity class +itself. This is useful for cases where you want to keep your entity +and mapping information together but don't want to use annotations. +For this you just need to use the ``StaticPHPDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Now you just need to define a static function named +``loadMetadata($metadata)`` on your entity: + +.. code-block:: php + + mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string' + )); + } + } + +ClassMetadataBuilder +-------------------- + +To ease the use of the ClassMetadata API (which is very raw) there is a ``ClassMetadataBuilder`` that you can use. + +.. code-block:: php + + createField('id', 'integer')->isPrimaryKey()->generatedValue()->build(); + $builder->addField('username', 'string'); + } + } + +The API of the ClassMetadataBuilder has the following methods with a fluent interface: + +- ``addField($name, $type, array $mapping)`` +- ``setMappedSuperclass()`` +- ``setReadOnly()`` +- ``setCustomRepositoryClass($className)`` +- ``setTable($name)`` +- ``addIndex(array $columns, $indexName)`` +- ``addUniqueConstraint(array $columns, $constraintName)`` +- ``addNamedQuery($name, $dqlQuery)`` +- ``setJoinedTableInheritance()`` +- ``setSingleTableInheritance()`` +- ``setDiscriminatorColumn($name, $type = 'string', $length = 255)`` +- ``addDiscriminatorMapClass($name, $class)`` +- ``setChangeTrackingPolicyDeferredExplicit()`` +- ``setChangeTrackingPolicyNotify()`` +- ``addLifecycleEvent($methodName, $event)`` +- ``addManyToOne($name, $targetEntity, $inversedBy = null)`` +- ``addInverseOneToOne($name, $targetEntity, $mappedBy)`` +- ``addOwningOneToOne($name, $targetEntity, $inversedBy = null)`` +- ``addOwningManyToMany($name, $targetEntity, $inversedBy = null)`` +- ``addInverseManyToMany($name, $targetEntity, $mappedBy)`` +- ``addOneToMany($name, $targetEntity, $mappedBy)`` + +It also has several methods that create builders (which are necessary for advanced mappings): + +- ``createField($name, $type)`` returns a ``FieldBuilder`` instance +- ``createManyToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance +- ``createOneToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance +- ``createManyToMany($name, $targetEntity)`` returns an ``ManyToManyAssociationBuilder`` instance +- ``createOneToMany($name, $targetEntity)`` returns an ``OneToManyAssociationBuilder`` instance + +ClassMetadataInfo API +--------------------- + +The ``ClassMetadataInfo`` class is the base data object for storing +the mapping metadata for a single entity. It contains all the +getters and setters you need populate and retrieve information for +an entity. + +General Setters +~~~~~~~~~~~~~~~ + + +- ``setTableName($tableName)`` +- ``setPrimaryTable(array $primaryTableDefinition)`` +- ``setCustomRepositoryClass($repositoryClassName)`` +- ``setIdGeneratorType($generatorType)`` +- ``setIdGenerator($generator)`` +- ``setSequenceGeneratorDefinition(array $definition)`` +- ``setChangeTrackingPolicy($policy)`` +- ``setIdentifier(array $identifier)`` + +Inheritance Setters +~~~~~~~~~~~~~~~~~~~ + + +- ``setInheritanceType($type)`` +- ``setSubclasses(array $subclasses)`` +- ``setParentClasses(array $classNames)`` +- ``setDiscriminatorColumn($columnDef)`` +- ``setDiscriminatorMap(array $map)`` + +Field Mapping Setters +~~~~~~~~~~~~~~~~~~~~~ + + +- ``mapField(array $mapping)`` +- ``mapOneToOne(array $mapping)`` +- ``mapOneToMany(array $mapping)`` +- ``mapManyToOne(array $mapping)`` +- ``mapManyToMany(array $mapping)`` + +Lifecycle Callback Setters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``addLifecycleCallback($callback, $event)`` +- ``setLifecycleCallbacks(array $callbacks)`` + +Versioning Setters +~~~~~~~~~~~~~~~~~~ + + +- ``setVersionMapping(array &$mapping)`` +- ``setVersioned($bool)`` +- ``setVersionField()`` + +General Getters +~~~~~~~~~~~~~~~ + + +- ``getTableName()`` +- ``getSchemaName()`` +- ``getTemporaryIdTableName()`` + +Identifier Getters +~~~~~~~~~~~~~~~~~~ + + +- ``getIdentifierColumnNames()`` +- ``usesIdGenerator()`` +- ``isIdentifier($fieldName)`` +- ``isIdGeneratorIdentity()`` +- ``isIdGeneratorSequence()`` +- ``isIdGeneratorTable()`` +- ``isIdentifierNatural()`` +- ``getIdentifierFieldNames()`` +- ``getSingleIdentifierFieldName()`` +- ``getSingleIdentifierColumnName()`` + +Inheritance Getters +~~~~~~~~~~~~~~~~~~~ + + +- ``isInheritanceTypeNone()`` +- ``isInheritanceTypeJoined()`` +- ``isInheritanceTypeSingleTable()`` +- ``isInheritanceTypeTablePerClass()`` +- ``isInheritedField($fieldName)`` +- ``isInheritedAssociation($fieldName)`` + +Change Tracking Getters +~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``isChangeTrackingDeferredExplicit()`` +- ``isChangeTrackingDeferredImplicit()`` +- ``isChangeTrackingNotify()`` + +Field & Association Getters +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``isUniqueField($fieldName)`` +- ``isNullable($fieldName)`` +- ``getColumnName($fieldName)`` +- ``getFieldMapping($fieldName)`` +- ``getAssociationMapping($fieldName)`` +- ``getAssociationMappings()`` +- ``getFieldName($columnName)`` +- ``hasField($fieldName)`` +- ``getColumnNames(array $fieldNames = null)`` +- ``getTypeOfField($fieldName)`` +- ``getTypeOfColumn($columnName)`` +- ``hasAssociation($fieldName)`` +- ``isSingleValuedAssociation($fieldName)`` +- ``isCollectionValuedAssociation($fieldName)`` + +Lifecycle Callback Getters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``hasLifecycleCallbacks($lifecycleEvent)`` +- ``getLifecycleCallbacks($event)`` + +ClassMetadata API +----------------- + +The ``ClassMetadata`` class extends ``ClassMetadataInfo`` and adds +the runtime functionality required by Doctrine. It adds a few extra +methods related to runtime reflection for working with the entities +themselves. + + +- ``getReflectionClass()`` +- ``getReflectionProperties()`` +- ``getReflectionProperty($name)`` +- ``getSingleIdReflectionProperty()`` +- ``getIdentifierValues($entity)`` +- ``setIdentifierValues($entity, $id)`` +- ``setFieldValue($entity, $field, $value)`` +- ``getFieldValue($entity, $field)`` + + diff --git a/vendor/doctrine/orm/docs/en/reference/query-builder.rst b/vendor/doctrine/orm/docs/en/reference/query-builder.rst new file mode 100644 index 00000000000..57eee179f63 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/query-builder.rst @@ -0,0 +1,565 @@ +The QueryBuilder +================ + +A ``QueryBuilder`` provides an API that is designed for +conditionally constructing a DQL query in several steps. + +It provides a set of classes and methods that is able to +programmatically build queries, and also provides a fluent API. +This means that you can change between one methodology to the other +as you want, and also pick one if you prefer. + +Constructing a new QueryBuilder object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The same way you build a normal Query, you build a ``QueryBuilder`` +object, just providing the correct method name. Here is an example +how to build a ``QueryBuilder`` object: + +.. code-block:: php + + createQueryBuilder(); + +Once you have created an instance of QueryBuilder, it provides a +set of useful informative functions that you can use. One good +example is to inspect what type of object the ``QueryBuilder`` is. + +.. code-block:: php + + getType(); // Prints: 0 + +There're currently 3 possible return values for ``getType()``: + + +- ``QueryBuilder::SELECT``, which returns value 0 +- ``QueryBuilder::DELETE``, returning value 1 +- ``QueryBuilder::UPDATE``, which returns value 2 + +It is possible to retrieve the associated ``EntityManager`` of the +current ``QueryBuilder``, its DQL and also a ``Query`` object when +you finish building your DQL. + +.. code-block:: php + + getEntityManager(); + + // example4: retrieve the DQL string of what was defined in QueryBuilder + $dql = $qb->getDql(); + + // example5: retrieve the associated Query object with the processed DQL + $q = $qb->getQuery(); + +Internally, ``QueryBuilder`` works with a DQL cache to increase +performance. Any changes that may affect the generated DQL actually +modifies the state of ``QueryBuilder`` to a stage we call +STATE\_DIRTY. One ``QueryBuilder`` can be in two different states: + + +- ``QueryBuilder::STATE_CLEAN``, which means DQL haven't been + altered since last retrieval or nothing were added since its + instantiation +- ``QueryBuilder::STATE_DIRTY``, means DQL query must (and will) + be processed on next retrieval + +Working with QueryBuilder +~~~~~~~~~~~~~~~~~~~~~~~~~ + + +High level API methods +^^^^^^^^^^^^^^^^^^^^^^ + +To simplify even more the way you build a query in Doctrine, we can take +advantage of what we call Helper methods. For all base code, there +is a set of useful methods to simplify a programmer's life. To +illustrate how to work with them, here is the same example 6 +re-written using ``QueryBuilder`` helper methods: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.id = ?1') + ->orderBy('u.name', 'ASC'); + +``QueryBuilder`` helper methods are considered the standard way to +build DQL queries. Although it is supported, it should be avoided +to use string based queries and greatly encouraged to use +``$qb->expr()->*`` methods. Here is a converted example 8 to +suggested standard way to build queries: + +.. code-block:: php + + select(array('u')) // string 'u' is converted to array internally + ->from('User', 'u') + ->where($qb->expr()->orX( + $qb->expr()->eq('u.id', '?1'), + $qb->expr()->like('u.nickname', '?2') + )) + ->orderBy('u.surname', 'ASC')); + +Here is a complete list of helper methods available in ``QueryBuilder``: + +.. code-block:: php + + select('u') + // Example - $qb->select(array('u', 'p')) + // Example - $qb->select($qb->expr()->select('u', 'p')) + public function select($select = null); + + // addSelect does not override previous calls to select + // + // Example - $qb->select('u'); + // ->addSelect('p.area_code'); + public function addSelect($select = null); + + // Example - $qb->delete('User', 'u') + public function delete($delete = null, $alias = null); + + // Example - $qb->update('Group', 'g') + public function update($update = null, $alias = null); + + // Example - $qb->set('u.firstName', $qb->expr()->literal('Arnold')) + // Example - $qb->set('u.numChilds', 'u.numChilds + ?1') + // Example - $qb->set('u.numChilds', $qb->expr()->sum('u.numChilds', '?1')) + public function set($key, $value); + + // Example - $qb->from('Phonenumber', 'p') + // Example - $qb->from('Phonenumber', 'p', 'p.id') + public function from($from, $alias, $indexBy = null); + + // Example - $qb->join('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1')) + // Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1') + // Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id') + public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null); + + // Example - $qb->innerJoin('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1')) + // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1') + // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id') + public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null); + + // Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55)) + // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55') + // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55', 'p.id') + public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null); + + // NOTE: ->where() overrides all previously set conditions + // + // Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2')) + // Example - $qb->where($qb->expr()->andX($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2'))) + // Example - $qb->where('u.firstName = ?1 AND u.surname = ?2') + public function where($where); + + // NOTE: ->andWhere() can be used directly, without any ->where() before + // + // Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0')) + public function andWhere($where); + + // Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10)); + public function orWhere($where); + + // NOTE: -> groupBy() overrides all previously set grouping conditions + // + // Example - $qb->groupBy('u.id') + public function groupBy($groupBy); + + // Example - $qb->addGroupBy('g.name') + public function addGroupBy($groupBy); + + // NOTE: -> having() overrides all previously set having conditions + // + // Example - $qb->having('u.salary >= ?1') + // Example - $qb->having($qb->expr()->gte('u.salary', '?1')) + public function having($having); + + // Example - $qb->andHaving($qb->expr()->gt($qb->expr()->count('u.numChild'), 0)) + public function andHaving($having); + + // Example - $qb->orHaving($qb->expr()->lte('g.managerLevel', '100')) + public function orHaving($having); + + // NOTE: -> orderBy() overrides all previously set ordering conditions + // + // Example - $qb->orderBy('u.surname', 'DESC') + public function orderBy($sort, $order = null); + + // Example - $qb->addOrderBy('u.firstName') + public function addOrderBy($sort, $order = null); // Default $order = 'ASC' + } + +Binding parameters to your query +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Doctrine supports dynamic binding of parameters to your query, +similar to preparing queries. You can use both strings and numbers +as placeholders, although both have a slightly different syntax. +Additionally, you must make your choice: Mixing both styles is not +allowed. Binding parameters can simply be achieved as follows: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.id = ?1') + ->orderBy('u.name', 'ASC') + ->setParameter(1, 100); // Sets ?1 to 100, and thus we will fetch a user with u.id = 100 + +You are not forced to enumerate your placeholders as the +alternative syntax is available: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.id = :identifier') + ->orderBy('u.name', 'ASC') + ->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100 + +Note that numeric placeholders start with a ? followed by a number +while the named placeholders start with a : followed by a string. + +Calling ``setParameter()`` automatically infers which type you are setting as +value. This works for integers, arrays of strings/integers, DateTime instances +and for managed entities. If you want to set a type explicitly you can call +the third argument to ``setParameter()`` explicitly. It accepts either a PDO +type or a DBAL Type name for conversion. + +If you've got several parameters to bind to your query, you can +also use setParameters() instead of setParameter() with the +following syntax: + +.. code-block:: php + + setParameters(array(1 => 'value for ?1', 2 => 'value for ?2')); + +Getting already bound parameters is easy - simply use the above +mentioned syntax with "getParameter()" or "getParameters()": + +.. code-block:: php + + getParameters(); + // $params instanceof \Doctrine\Common\Collections\ArrayCollection + + // Equivalent to + $param = $qb->getParameter(1); + // $param instanceof \Doctrine\ORM\Query\Parameter + +Note: If you try to get a parameter that was not bound yet, +getParameter() simply returns NULL. + +The API of a Query Parameter is: + +.. code-block:: php + + namespace Doctrine\ORM\Query; + + class Parameter + { + public function getName(); + public function getValue(); + public function getType(); + public function setValue($value, $type = null); + } + +Limiting the Result +^^^^^^^^^^^^^^^^^^^ + +To limit a result the query builder has some methods in common with +the Query object which can be retrieved from ``EntityManager#createQuery()``. + +.. code-block:: php + + add('select', 'u') + ->add('from', 'User u') + ->add('orderBy', 'u.name ASC') + ->setFirstResult( $offset ) + ->setMaxResults( $limit ); + +Executing a Query +^^^^^^^^^^^^^^^^^ + +The QueryBuilder is a builder object only, it has no means of actually +executing the Query. Additionally a set of parameters such as query hints +cannot be set on the QueryBuilder itself. This is why you always have to convert +a querybuilder instance into a Query object: + +.. code-block:: php + + getQuery(); + + // Set additional Query options + $query->setQueryHint('foo', 'bar'); + $query->useResultCache('my_cache_id'); + + // Execute Query + $result = $query->getResult(); + $single = $query->getSingleResult(); + $array = $query->getArrayResult(); + $scalar = $query->getScalarResult(); + $singleScalar = $query->getSingleScalarResult(); + +The Expr class +^^^^^^^^^^^^^^ + +To workaround some of the issues that ``add()`` method may cause, +Doctrine created a class that can be considered as a helper for +building expressions. This class is called ``Expr``, which provides a +set of useful methods to help build expressions: + +.. code-block:: php + + add('select', new Expr\Select(array('u'))) + ->add('from', new Expr\From('User', 'u')) + ->add('where', $qb->expr()->orX( + $qb->expr()->eq('u.id', '?1'), + $qb->expr()->like('u.nickname', '?2') + )) + ->add('orderBy', new Expr\OrderBy('u.name', 'ASC')); + +Although it still sounds complex, the ability to programmatically +create conditions are the main feature of ``Expr``. Here it is a +complete list of supported helper methods available: + +.. code-block:: php + + expr()->andX($cond1 [, $condN])->add(...)->... + public function andX($x = null); // Returns Expr\AndX instance + + // Example - $qb->expr()->orX($cond1 [, $condN])->add(...)->... + public function orX($x = null); // Returns Expr\OrX instance + + + /** Comparison objects **/ + + // Example - $qb->expr()->eq('u.id', '?1') => u.id = ?1 + public function eq($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->neq('u.id', '?1') => u.id <> ?1 + public function neq($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->lt('u.id', '?1') => u.id < ?1 + public function lt($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->lte('u.id', '?1') => u.id <= ?1 + public function lte($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->gt('u.id', '?1') => u.id > ?1 + public function gt($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->gte('u.id', '?1') => u.id >= ?1 + public function gte($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->isNull('u.id') => u.id IS NULL + public function isNull($x); // Returns string + + // Example - $qb->expr()->isNotNull('u.id') => u.id IS NOT NULL + public function isNotNull($x); // Returns string + + + /** Arithmetic objects **/ + + // Example - $qb->expr()->prod('u.id', '2') => u.id * 2 + public function prod($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->diff('u.id', '2') => u.id - 2 + public function diff($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->sum('u.id', '2') => u.id + 2 + public function sum($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->quot('u.id', '2') => u.id / 2 + public function quot($x, $y); // Returns Expr\Math instance + + + /** Pseudo-function objects **/ + + // Example - $qb->expr()->exists($qb2->getDql()) + public function exists($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->all($qb2->getDql()) + public function all($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->some($qb2->getDql()) + public function some($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->any($qb2->getDql()) + public function any($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->not($qb->expr()->eq('u.id', '?1')) + public function not($restriction); // Returns Expr\Func instance + + // Example - $qb->expr()->in('u.id', array(1, 2, 3)) + // Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception. + // Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1 (see section above) + public function in($x, $y); // Returns Expr\Func instance + + // Example - $qb->expr()->notIn('u.id', '2') + public function notIn($x, $y); // Returns Expr\Func instance + + // Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%')) + public function like($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->notLike('u.firstname', $qb->expr()->literal('Gui%')) + public function notLike($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->between('u.id', '1', '10') + public function between($val, $x, $y); // Returns Expr\Func + + + /** Function objects **/ + + // Example - $qb->expr()->trim('u.firstname') + public function trim($x); // Returns Expr\Func + + // Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname')) + public function concat($x, $y); // Returns Expr\Func + + // Example - $qb->expr()->substring('u.firstname', 0, 1) + public function substring($x, $from, $len); // Returns Expr\Func + + // Example - $qb->expr()->lower('u.firstname') + public function lower($x); // Returns Expr\Func + + // Example - $qb->expr()->upper('u.firstname') + public function upper($x); // Returns Expr\Func + + // Example - $qb->expr()->length('u.firstname') + public function length($x); // Returns Expr\Func + + // Example - $qb->expr()->avg('u.age') + public function avg($x); // Returns Expr\Func + + // Example - $qb->expr()->max('u.age') + public function max($x); // Returns Expr\Func + + // Example - $qb->expr()->min('u.age') + public function min($x); // Returns Expr\Func + + // Example - $qb->expr()->abs('u.currentBalance') + public function abs($x); // Returns Expr\Func + + // Example - $qb->expr()->sqrt('u.currentBalance') + public function sqrt($x); // Returns Expr\Func + + // Example - $qb->expr()->count('u.firstname') + public function count($x); // Returns Expr\Func + + // Example - $qb->expr()->countDistinct('u.surname') + public function countDistinct($x); // Returns Expr\Func + } + + +Low Level API +^^^^^^^^^^^^^ + +Now we have describe the low level (thought of as the +hardcore method) of creating queries. It may be useful to work at +this level for optimization purposes, but most of the time it is +preferred to work at a higher level of abstraction. + +All helper methods in ``QueryBuilder`` actually rely on a single +one: ``add()``. This method is responsible of building every piece +of DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and +``$append`` (default=false) + + +- ``$dqlPartName``: Where the ``$dqlPart`` should be placed. + Possible values: select, from, where, groupBy, having, orderBy +- ``$dqlPart``: What should be placed in ``$dqlPartName``. Accepts + a string or any instance of ``Doctrine\ORM\Query\Expr\*`` +- ``$append``: Optional flag (default=false) if the ``$dqlPart`` + should override all previously defined items in ``$dqlPartName`` or + not (no effect on the ``where`` and ``having`` DQL query parts, + which always override all previously defined items) + +- + +.. code-block:: php + + add('select', 'u') + ->add('from', 'User u') + ->add('where', 'u.id = ?1') + ->add('orderBy', 'u.name ASC'); + +Expr\* classes +^^^^^^^^^^^^^^ + +When you call ``add()`` with string, it internally evaluates to an +instance of ``Doctrine\ORM\Query\Expr\Expr\*`` class. Here is the +same query of example 6 written using +``Doctrine\ORM\Query\Expr\Expr\*`` classes: + +.. code-block:: php + + add('select', new Expr\Select(array('u'))) + ->add('from', new Expr\From('User', 'u')) + ->add('where', new Expr\Comparison('u.id', '=', '?1')) + ->add('orderBy', new Expr\OrderBy('u.name', 'ASC')); + +Of course this is the hardest way to build a DQL query in Doctrine. +To simplify some of these efforts, we introduce what we call as +``Expr`` helper class. + diff --git a/vendor/doctrine/orm/docs/en/reference/second-level-cache.rst b/vendor/doctrine/orm/docs/en/reference/second-level-cache.rst new file mode 100644 index 00000000000..f69314dc7e7 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/second-level-cache.rst @@ -0,0 +1,731 @@ +The Second Level Cache +====================== + +.. note:: + + The second level cache functionality is marked as experimental for now. It + is a very complex feature and we cannot guarantee yet that it works stable + in all cases. + +The Second Level Cache is designed to reduce the amount of necessary database access. +It sits between your application and the database to avoid the number of database hits as much as possible. + +When turned on, entities will be first searched in cache and if they are not found, +a database query will be fired an then the entity result will be stored in a cache provider. + +There are some flavors of caching available, but is better to cache read-only data. + +Be aware that caches are not aware of changes made to the persistent store by another application. +They can, however, be configured to regularly expire cached data. + + +Caching Regions +--------------- + +Second level cache does not store instances of an entity, instead it caches only entity identifier and values. +Each entity class, collection association and query has its region, where values of each instance are stored. + +Caching Regions are specific region into the cache provider that might store entities, collection or queries. +Each cache region resides in a specific cache namespace and has its own lifetime configuration. + +Notice that when caching collection and queries only identifiers are stored. +The entity values will be stored in its own region + +Something like below for an entity region : + +.. code-block:: php + + ['id'=> 1, 'name' => 'FooBar', 'associationName'=>null], + 'region_name:entity_2_hash' => ['id'=> 2, 'name' => 'Foo', 'associationName'=>['id'=>11]], + 'region_name:entity_3_hash' => ['id'=> 3, 'name' => 'Bar', 'associationName'=>['id'=>22]] + ]; + + +If the entity holds a collection that also needs to be cached. +An collection region could look something like : + +.. code-block:: php + + ['ownerId'=> 1, 'list' => [1, 2, 3]], + 'region_name:entity_2_coll_assoc_name_hash' => ['ownerId'=> 2, 'list' => [2, 3]], + 'region_name:entity_3_coll_assoc_name_hash' => ['ownerId'=> 3, 'list' => [2, 4]] + ]; + +A query region might be something like : + +.. code-block:: php + + ['list' => [1, 2, 3]], + 'region_name:query_2_hash' => ['list' => [2, 3]], + 'region_name:query_3_hash' => ['list' => [2, 4]] + ]; + + +.. note:: + + The following data structures represents now the cache will looks like, this is not actual cached data. + + +.. _reference-second-level-cache-regions: + +Cache Regions +------------- + +``Doctrine\ORM\Cache\Region\DefaultRegion`` It's the default implementation. + A simplest cache region compatible with all doctrine-cache drivers but does not support locking. + +``Doctrine\ORM\Cache\Region`` and ``Doctrine\ORM\Cache\ConcurrentRegion`` +Defines contracts that should be implemented by a cache provider. + +It allows you to provide your own cache implementation that might take advantage of specific cache driver. + +If you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise. + + +Cache region +~~~~~~~~~~~~ + +Defines a contract for accessing a particular region. + +``Doctrine\ORM\Cache\Region`` + +Defines a contract for accessing a particular cache region. + +`See API Doc `_. + +Concurrent cache region +~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Doctrine\ORM\Cache\ConcurrentRegion`` is designed to store concurrently managed data region. +By default, Doctrine provides a very simple implementation based on file locks ``Doctrine\ORM\Cache\Region\FileLockRegion``. + +If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region. + +``Doctrine\ORM\Cache\ConcurrentRegion`` + +Defines contract for concurrently managed data region. + +`See API Doc `_. + +Timestamp region +~~~~~~~~~~~~~~~~ + +``Doctrine\ORM\Cache\TimestampRegion`` + +Tracks the timestamps of the most recent updates to particular entity. + +`See API Doc `_. + +.. _reference-second-level-cache-mode: + +Caching mode +------------ + +* ``READ_ONLY`` (DEFAULT) + + * Can do reads, inserts and deletes, cannot perform updates or employ any locks. + * Useful for data that is read frequently but never updated. + * Best performer. + * It is Simple. + +* ``NONSTRICT_READ_WRITE`` + + * Read Write Cache doesn’t employ any locks but can do reads, inserts, updates and deletes. + * Good if the application needs to update data rarely. + + +* ``READ_WRITE`` + + * Read Write cache employs locks before update/delete. + * Use if data needs to be updated. + * Slowest strategy. + * To use it a the cache region implementation must support locking. + + +Built-in cached persisters +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Cached persisters are responsible to access cache regions. + + +-----------------------+-------------------------------------------------------------------------------+ + | Cache Usage | Persister | + +=======================+===============================================================================+ + | READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\ReadOnlyCachedEntityPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\ReadWriteCachedEntityPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\NonStrictReadWriteCachedEntityPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\ReadOnlyCachedCollectionPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\ReadWriteCachedCollectionPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\NonStrictReadWriteCacheCollectionPersister | + +-----------------------+-------------------------------------------------------------------------------+ + +Configuration +------------- +Doctrine allows you to specify configurations and some points of extension for the second-level-cache + + +Enable Second Level Cache +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To enable the second-level-cache, you should provide a cache factory +``\Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation. + +.. code-block:: php + + setSecondLevelCacheEnabled(); + + // Cache factory + $config->getSecondLevelCacheConfiguration() + ->setCacheFactory($factory); + + +Cache Factory +~~~~~~~~~~~~~ + +Cache Factory is the main point of extension. + +It allows you to provide a specific implementation of the following components : + +* ``QueryCache`` Store and retrieve query cache results. +* ``CachedEntityPersister`` Store and retrieve entity results. +* ``CachedCollectionPersister`` Store and retrieve query results. +* ``EntityHydrator`` Transform an entity into a cache entry and cache entry into entities +* ``CollectionHydrator`` Transform a collection into a cache entry and cache entry into collection + +`See API Doc `_. + +Region Lifetime +~~~~~~~~~~~~~~~ + +To specify a default lifetime for all regions or specify a different lifetime for a specific region. + +.. code-block:: php + + getSecondLevelCacheConfiguration(); + $regionConfig = $cacheConfig->getRegionsConfiguration(); + + // Cache Region lifetime + $regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region; In seconds + $regionConfig->setDefaultLifetime(7200); // Default time to live; In seconds + + +Cache Log +~~~~~~~~~ +By providing a cache logger you should be able to get information about all cache operations such as hits, misses and puts. + +``\Doctrine\ORM\Cache\Logging\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics. + + .. code-block:: php + + setSecondLevelCacheEnabled(true); + $config->getSecondLevelCacheConfiguration() + ->setCacheLogger($logger); + + + // Collect cache statistics + + // Get the number of entries successfully retrieved from a specific region. + $logger->getRegionHitCount('my_entity_region'); + + // Get the number of cached entries *not* found in a specific region. + $logger->getRegionMissCount('my_entity_region'); + + // Get the number of cacheable entries put in cache. + $logger->getRegionPutCount('my_entity_region'); + + // Get the total number of put in all regions. + $logger->getPutCount(); + + // Get the total number of entries successfully retrieved from all regions. + $logger->getHitCount(); + + // Get the total number of cached entries *not* found in all regions. + $logger->getMissCount(); + +If you want to get more information you should implement ``\Doctrine\ORM\Cache\Logging\CacheLogger``. +and collect all information you want. + +`See API Doc `_. + + +Entity cache definition +----------------------- +* Entity cache configuration allows you to define the caching strategy and region for an entity. + + * ``usage`` Specifies the caching strategy: ``READ_ONLY``, ``NONSTRICT_READ_WRITE``, ``READ_WRITE``. see :ref:`reference-second-level-cache-mode` + * ``region`` Optional value that specifies the name of the second level cache region. + + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + .. code-block:: yaml + + Country: + type: entity + cache: + usage : READ_ONLY + region : my_entity_region + id: + id: + type: integer + id: true + generator: + strategy: IDENTITY + fields: + name: + type: string + + +Association cache definition +---------------------------- +The most common use case is to cache entities. But we can also cache relationships. +It caches the primary keys of association and cache each element will be cached into its region. + + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + State: + type: entity + cache: + usage : NONSTRICT_READ_WRITE + id: + id: + type: integer + id: true + generator: + strategy: IDENTITY + fields: + name: + type: string + + manyToOne: + state: + targetEntity: Country + joinColumns: + country_id: + referencedColumnName: id + cache: + usage : NONSTRICT_READ_WRITE + + oneToMany: + cities: + targetEntity:City + mappedBy: state + cache: + usage : NONSTRICT_READ_WRITE + + +> Note: for this to work, the target entity must also be marked as cacheable. + +Cache usage +~~~~~~~~~~~ + +Basic entity cache + +.. code-block:: php + + persist(new Country($name)); + $em->flush(); // Hit database to insert the row and put into cache + + $em->clear(); // Clear entity manager + + $country1 = $em->find('Country', 1); // Retrieve item from cache + + $country->setName("New Name"); + $em->persist($country); + $em->flush(); // Hit database to update the row and update cache + + $em->clear(); // Clear entity manager + + $country2 = $em->find('Country', 1); // Retrieve item from cache + // Notice that $country1 and $country2 are not the same instance. + + +Association cache + +.. code-block:: php + + persist(new State($name, $country)); + $em->flush(); + + // Clear entity manager + $em->clear(); + + // Retrieve item from cache + $state = $em->find('State', 1); + + // Hit database to update the row and update cache entry + $state->setName("New Name"); + $em->persist($state); + $em->flush(); + + // Create a new collection item + $city = new City($name, $state); + $state->addCity($city); + + // Hit database to insert new collection item, + // put entity and collection cache into cache. + $em->persist($city); + $em->persist($state); + $em->flush(); + + // Clear entity manager + $em->clear(); + + // Retrieve item from cache + $state = $em->find('State', 1); + + // Retrieve association from cache + $country = $state->getCountry(); + + // Retrieve collection from cache + $cities = $state->getCities(); + + echo $country->getName(); + echo $state->getName(); + + // Retrieve each collection item from cache + foreach ($cities as $city) { + echo $city->getName(); + } + +.. note:: + + Notice that all entities should be marked as cacheable. + +Using the query cache +--------------------- + +The second level cache stores the entities, associations and collections. +The query cache stores the results of the query but as identifiers, entity values are actually stored in the 2nd level cache. + +.. note:: + + Query cache should always be used in conjunction with the second-level-cache for those entities which should be cached. + +.. code-block:: php + + createQuery('SELECT c FROM Country c ORDER BY c.name') + ->setCacheable(true) + ->getResult(); + + $em->clear() + + // Check if query result is valid and load entities from cache + $result2 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name') + ->setCacheable(true) + ->getResult(); + +Cache mode +~~~~~~~~~~ + +The Cache Mode controls how a particular query interacts with the second-level cache: + +* ``Cache::MODE_GET`` - May read items from the cache, but will not add items. +* ``Cache::MODE_PUT`` - Will never read items from the cache, but will add items to the cache as it reads them from the database. +* ``Cache::MODE_NORMAL`` - May read items from the cache, and add items to the cache. +* ``Cache::MODE_REFRESH`` - The query will never read items from the cache, but will refresh items to the cache as it reads them from the database. + +.. code-block:: php + + createQuery('SELECT c FROM Country c ORDER BY c.name') + ->setCacheMode(Cache::MODE_GET) + ->setCacheable(true) + ->getResult(); + +.. note:: + + The the default query cache mode is ```Cache::MODE_NORMAL``` + +DELETE / UPDATE queries +~~~~~~~~~~~~~~~~~~~~~~~ + +DQL UPDATE / DELETE statements are ported directly into a database and bypass the second-level cache, +Entities that are already cached will NOT be invalidated. +However the cached data could be evicted using the cache API or an special query hint. + + +Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT`` + +.. code-block:: php + + _em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1") + ->setHint(Query::HINT_CACHE_EVICT, true) + ->execute(); + + +Execute the ``UPDATE`` and invalidate ``all cache entries`` using the cache API + +.. code-block:: php + + _em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1") + ->execute(); + // Invoke Cache API + $em->getCache()->evictEntityRegion('Entity\Country'); + + +Execute the ``UPDATE`` and invalidate ``a specific cache entry`` using the cache API + +.. code-block:: php + + _em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1") + ->execute(); + // Invoke Cache API + $em->getCache()->evictEntity('Entity\Country', 1); + +Using the repository query cache +--------------------- + +As well as ``Query Cache`` all persister queries store only identifier values for an individual query. +All persister use a single timestamps cache region keeps track of the last update for each persister, +When a query is loaded from cache, the timestamp region is checked for the last update for that persister. +Using the last update timestamps as part of the query key invalidate the cache key when an update occurs. + +.. code-block:: php + + getRepository('Entity\Country')->findAll(); + + // load from query and entities from cache.. + $entities = $em->getRepository('Entity\Country')->findAll(); + + // update the timestamp cache region for Country + $em->persist(new Country('zombieland')); + $em->flush(); + $em->clear(); + + // Reload from database. + // At this point the query cache key if not logger valid, the select goes straight + $entities = $em->getRepository('Entity\Country')->findAll(); + +Cache API +--------- + +Caches are not aware of changes made by another application. +However, you can use the cache API to check / invalidate cache entries. + +.. code-block:: php + + getCache(); + + $cache->containsEntity('Entity\State', 1) // Check if the cache exists + $cache->evictEntity('Entity\State', 1); // Remove an entity from cache + $cache->evictEntityRegion('Entity\State'); // Remove all entities from cache + + $cache->containsCollection('Entity\State', 'cities', 1); // Check if the cache exists + $cache->evictCollection('Entity\State', 'cities', 1); // Remove an entity collection from cache + $cache->evictCollectionRegion('Entity\State', 'cities'); // Remove all collections from cache + +Limitations +----------- + +Composite primary key +~~~~~~~~~~~~~~~~~~~~~ + +Composite primary key are supported by second level cache, +however when one of the keys is an association the cached entity should always be retrieved using the association identifier. +For performance reasons the cache API does not extract from composite primary key. + +.. code-block:: php + + find('Article', 1); + + // Supported + /* @var $article Article */ + $article = $em->find('Article', $article); + + // Supported + $id = array('source' => 1, 'target' => 2); + $reference = $em->find('Reference', $id); + + // NOT Supported + $id = array('source' => new Article(1), 'target' => new Article(2)); + $reference = $em->find('Reference', $id); + +Distributed environments +~~~~~~~~~~~~~~~~~~~~~~~~ + +Some cache driver are not meant to be used in a distributed environment. +Load-balancer for distributing workloads across multiple computing resources +should be used in conjunction with distributed caching system such as memcached, redis, riak ... + +Caches should be used with care when using a load-balancer if you don't share the cache. +While using APC or any file based cache update occurred in a specific machine would not reflect to the cache in other machines. + + +Paginator +~~~~~~~~~ + +Count queries generated by ``Doctrine\ORM\Tools\Pagination\Paginator`` are not cached by second-level cache. +Although entities and query result are cached count queries will hit the database every time. diff --git a/vendor/doctrine/orm/docs/en/reference/security.rst b/vendor/doctrine/orm/docs/en/reference/security.rst new file mode 100644 index 00000000000..efc0989cfbf --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/security.rst @@ -0,0 +1,151 @@ +Security +======== + +The Doctrine library is operating very close to your database and as such needs +to handle and make assumptions about SQL injection vulnerabilities. + +It is vital that you understand how Doctrine approaches security, because +we cannot protect you from SQL injection. + +Please also read the documentation chapter on Security in Doctrine DBAL. This +page only handles Security issues in the ORM. + +- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst) + +If you find a Security bug in Doctrine, please report it on Jira and change the +Security Level to "Security Issues". It will be visible to Doctrine Core +developers and you only. + +User input and Doctrine ORM +--------------------------- + +The ORM is much better at protecting against SQL injection than the DBAL alone. +You can consider the following APIs to be safe from SQL injection: + +- ``\Doctrine\ORM\EntityManager#find()`` and ``getReference()``. +- All values on Objects inserted and updated through ``Doctrine\ORM\EntityManager#persist()`` +- All find methods on ``Doctrine\ORM\EntityRepository``. +- User Input set to DQL Queries or QueryBuilder methods through + - ``setParameter()`` or variants + - ``setMaxResults()`` + - ``setFirstResult()`` +- Queries through the Criteria API on ``Doctrine\ORM\PersistentCollection`` and + ``Doctrine\ORM\EntityRepository``. + +You are **NOT** save from SQL injection when using user input with: + +- Expression API of ``Doctrine\ORM\QueryBuilder`` +- Concatenating user input into DQL SELECT, UPDATE or DELETE statements or + Native SQL. + +This means SQL injections can only occur with Doctrine ORM when working with +Query Objects of any kind. The safe rule is to always use prepared statement +parameters for user objects when using a Query object. + +.. warning:: + + Insecure code follows, don't copy paste this. + +The following example shows insecure DQL usage: + +.. code-block:: php + + createQuery($dql); + $query->setParameter(1, $_GET['status']); + + +Preventing Mass Assignment Vulnerabilities +------------------------------------------ + +ORMs are very convenient for CRUD applications and Doctrine is no exception. +However CRUD apps are often vulnerable to mass assignment security problems +when implemented naively. + +Doctrine is not vulnerable to this problem out of the box, but you can easily +make your entities vulnerable to mass assignment when you add methods of +the kind ``updateFromArray()`` or ``updateFromJson()`` to them. A vulnerable +entity might look like this: + +.. code-block:: php + + $value) { + $this->$key = $value; + } + } + } + +Now the possiblity of mass-asignment exists on this entity and can +be exploitet by attackers to set the "isAdmin" flag to true on any +object when you pass the whole request data to this method like: + +.. code-block:: php + + fromArray($_POST); + + $entityManager->persist($entity); + $entityManager->flush(); + +You can spot this problem in this very simple example easily. However +in combination with frameworks and form libraries it might not be +so obvious when this issue arises. Be careful to avoid this +kind of mistake. + +How to fix this problem? You should always have a whitelist +of allowed key to set via mass assignment functions. + +.. code-block:: php + + public function fromArray(array $userInput, $allowedFields = array()) + { + foreach ($userInput as $key => $value) { + if (in_array($key, $allowedFields)) { + $this->$key = $value; + } + } + } diff --git a/vendor/doctrine/orm/docs/en/reference/tools.rst b/vendor/doctrine/orm/docs/en/reference/tools.rst new file mode 100644 index 00000000000..51f4573000e --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/tools.rst @@ -0,0 +1,528 @@ +Tools +===== + +Doctrine Console +---------------- + +The Doctrine Console is a Command Line Interface tool for simplifying common +administration tasks during the development of a project that uses Doctrine 2. + +Take a look at the :doc:`Installation and Configuration ` +chapter for more information how to setup the console command. + +Display Help Information +~~~~~~~~~~~~~~~~~~~~~~~~ + +Type ``php vendor/bin/doctrine`` on the command line and you should see an +overview of the available commands or use the --help flag to get +information on the available commands. If you want to know more +about the use of generate entities for example, you can call: + +.. code-block:: php + + $> php vendor/bin/doctrine orm:generate-entities --help + + +Configuration +~~~~~~~~~~~~~ + +Whenever the ``doctrine`` command line tool is invoked, it can +access all Commands that were registered by developer. There is no +auto-detection mechanism at work. The Doctrine binary +already registers all the commands that currently ship with +Doctrine DBAL and ORM. If you want to use additional commands you +have to register them yourself. + +All the commands of the Doctrine Console require access to the ``EntityManager`` +or ``DBAL`` Connection. You have to inject them into the console application +using so called Helper-Sets. This requires either the ``db`` +or the ``em`` helpers to be defined in order to work correctly. + +Whenever you invoke the Doctrine binary the current folder is searched for a +``cli-config.php`` file. This file contains the project specific configuration: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($conn) + )); + $cli->setHelperSet($helperSet); + +When dealing with the ORM package, the EntityManagerHelper is +required: + +.. code-block:: php + + new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + $cli->setHelperSet($helperSet); + +The HelperSet instance has to be generated in a separate file (i.e. +``cli-config.php``) that contains typical Doctrine bootstrap code +and predefines the needed HelperSet attributes mentioned above. A +sample ``cli-config.php`` file looks as follows: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + +It is important to define a correct HelperSet that Doctrine binary +script will ultimately use. The Doctrine Binary will automatically +find the first instance of HelperSet in the global variable +namespace and use this. + +.. note:: + + You have to adjust this snippet for your specific application or framework + and use their facilities to access the Doctrine EntityManager and + Connection Resources. + +Command Overview +~~~~~~~~~~~~~~~~ + +The following Commands are currently available: + + +- ``help`` Displays help for a command (?) +- ``list`` Lists commands +- ``dbal:import`` Import SQL file(s) directly to Database. +- ``dbal:run-sql`` Executes arbitrary SQL directly from the + command line. +- ``orm:clear-cache:metadata`` Clear all metadata cache of the + various cache drivers. +- ``orm:clear-cache:query`` Clear all query cache of the various + cache drivers. +- ``orm:clear-cache:result`` Clear result cache of the various + cache drivers. +- ``orm:convert-d1-schema`` Converts Doctrine 1.X schema into a + Doctrine 2.X schema. +- ``orm:convert-mapping`` Convert mapping information between + supported formats. +- ``orm:ensure-production-settings`` Verify that Doctrine is + properly configured for a production environment. +- ``orm:generate-entities`` Generate entity classes and method + stubs from your mapping information. +- ``orm:generate-proxies`` Generates proxy classes for entity + classes. +- ``orm:generate-repositories`` Generate repository classes from + your mapping information. +- ``orm:run-dql`` Executes arbitrary DQL directly from the command + line. +- ``orm:schema-tool:create`` Processes the schema and either + create it directly on EntityManager Storage Connection or generate + the SQL output. +- ``orm:schema-tool:drop`` Processes the schema and either drop + the database schema of EntityManager Storage Connection or generate + the SQL output. +- ``orm:schema-tool:update`` Processes the schema and either + update the database schema of EntityManager Storage Connection or + generate the SQL output. + +For these commands are also available aliases: + + +- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``. +- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``. +- ``orm:generate:entities`` is alias for ``orm:generate-entities``. +- ``orm:generate:proxies`` is alias for ``orm:generate-proxies``. +- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``. + +.. note:: + + Console also supports auto completion, for example, instead of + ``orm:clear-cache:query`` you can use just ``o:c:q``. + +Database Schema Generation +-------------------------- + +.. note:: + + SchemaTool can do harm to your database. It will drop or alter + tables, indexes, sequences and such. Please use this tool with + caution in development and not on a production server. It is meant + for helping you develop your Database Schema, but NOT with + migrating schema from A to B in production. A safe approach would + be generating the SQL on development server and saving it into SQL + Migration files that are executed manually on the production + server. + + SchemaTool assumes your Doctrine Project uses the given database on + its own. Update and Drop commands will mess with other tables if + they are not related to the current project that is using Doctrine. + Please be careful! + + +To generate your database schema from your Doctrine mapping files +you can use the ``SchemaTool`` class or the ``schema-tool`` Console +Command. + +When using the SchemaTool class directly, create your schema using +the ``createSchema()`` method. First create an instance of the +``SchemaTool`` and pass it an instance of the ``EntityManager`` +that you want to use to create the schema. This method receives an +array of ``ClassMetadataInfo`` instances. + +.. code-block:: php + + getClassMetadata('Entities\User'), + $em->getClassMetadata('Entities\Profile') + ); + $tool->createSchema($classes); + +To drop the schema you can use the ``dropSchema()`` method. + +.. code-block:: php + + dropSchema($classes); + +This drops all the tables that are currently used by your metadata +model. When you are changing your metadata a lot during development +you might want to drop the complete database instead of only the +tables of the current model to clean up with orphaned tables. + +.. code-block:: php + + dropSchema($classes, \Doctrine\ORM\Tools\SchemaTool::DROP_DATABASE); + +You can also use database introspection to update your schema +easily with the ``updateSchema()`` method. It will compare your +existing database schema to the passed array of +``ClassMetdataInfo`` instances. + +.. code-block:: php + + updateSchema($classes); + +If you want to use this functionality from the command line you can +use the ``schema-tool`` command. + +To create the schema use the ``create`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:create + +To drop the schema use the ``drop`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:drop + +If you want to drop and then recreate the schema then use both +options: + +.. code-block:: php + + $ php doctrine orm:schema-tool:drop + $ php doctrine orm:schema-tool:create + +As you would think, if you want to update your schema use the +``update`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:update + +All of the above commands also accept a ``--dump-sql`` option that +will output the SQL for the ran operation. + +.. code-block:: php + + $ php doctrine orm:schema-tool:create --dump-sql + +Before using the orm:schema-tool commands, remember to configure +your cli-config.php properly. + +.. note:: + + When using the Annotation Mapping Driver you have to either setup + your autoloader in the cli-config.php correctly to find all the + entities, or you can use the second argument of the + ``EntityManagerHelper`` to specify all the paths of your entities + (or mapping files), i.e. + ``new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em, $mappingPaths);`` + +Entity Generation +----------------- + +Generate entity classes and method stubs from your mapping information. + +.. code-block:: php + + $ php doctrine orm:generate-entities + $ php doctrine orm:generate-entities --update-entities + $ php doctrine orm:generate-entities --regenerate-entities + +This command is not suited for constant usage. It is a little helper and does +not support all the mapping edge cases very well. You still have to put work +in your entities after using this command. + +It is possible to use the EntityGenerator on code that you have already written. It will +not be lost. 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 don't 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. + +.. note:: + + 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! + + +Convert Mapping Information +--------------------------- + +Convert mapping information between supported formats. + +This is an **execute one-time** command. It should not be necessary for +you to call this method multiple times, especially when using the ``--from-database`` +flag. + +Converting an existing database 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. + +.. note:: + + 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. See the + docs on XML and YAML Mapping for an example how to register this metadata + drivers as primary mapping source. + +To convert some mapping information between the various supported +formats you can use the ``ClassMetadataExporter`` to get exporter +instances for the different formats: + +.. code-block:: php + + getExporter('yml', '/path/to/export/yml'); + +Now you can export some ``ClassMetadata`` instances: + +.. code-block:: php + + getClassMetadata('Entities\User'), + $em->getClassMetadata('Entities\Profile') + ); + $exporter->setMetadata($classes); + $exporter->export(); + +This functionality is also available from the command line to +convert your loaded mapping information to another format. The +``orm:convert-mapping`` command accepts two arguments, the type to +convert to and the path to generate it: + +.. code-block:: php + + $ php doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml + +Reverse Engineering +------------------- + +You can use the ``DatabaseDriver`` to reverse engineer a database +to an array of ``ClassMetadataInfo`` instances and generate YAML, +XML, etc. from them. + +.. note:: + + Reverse Engineering is a **one-time** process that can get you started with a project. + Converting an existing database schema into mapping files only detects 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. + +First you need to retrieve the metadata instances with the +``DatabaseDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl( + new \Doctrine\ORM\Mapping\Driver\DatabaseDriver( + $em->getConnection()->getSchemaManager() + ) + ); + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + +Now you can get an exporter instance and export the loaded metadata +to yml: + +.. code-block:: php + + getExporter('yml', '/path/to/export/yml'); + $exporter->setMetadata($metadata); + $exporter->export(); + +You can also reverse engineer a database using the +``orm:convert-mapping`` command: + +.. code-block:: php + + $ php doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml + +.. note:: + + Reverse Engineering is not always working perfectly + depending on special cases. It will only detect Many-To-One + relations (even if they are One-To-One) and will try to create + entities from Many-To-Many tables. It also has problems with naming + of foreign keys that have multiple column names. Any Reverse + Engineered Database-Schema needs considerable manual work to become + a useful domain model. + + +Runtime vs Development Mapping Validation +----------------------------------------- + +For performance reasons Doctrine 2 has to skip some of the +necessary validation of metadata mappings. You have to execute +this validation in your development workflow to verify the +associations are correctly defined. + +You can either use the Doctrine Command Line Tool: + +.. code-block:: php + + doctrine orm:validate-schema + +Or you can trigger the validation manually: + +.. code-block:: php + + validateMapping(); + + if (count($errors) > 0) { + // Lots of errors! + echo implode("\n\n", $errors); + } + +If the mapping is invalid the errors array contains a positive +number of elements with error messages. + +.. warning:: + + One mapping option that is not validated is the use of the referenced column name. + It has to point to the equivalent primary key otherwise Doctrine will not work. + +.. note:: + + One common error is to use a backlash in front of the + fully-qualified class-name. Whenever a FQCN is represented inside a + string (such as in your mapping definitions) you have to drop the + prefix backslash. PHP does this with ``get_class()`` or Reflection + methods for backwards compatibility reasons. + + +Adding own commands +------------------- + +You can also add your own commands on-top of the Doctrine supported +tools if you are using a manually built console script. + +To include a new command on Doctrine Console, you need to do modify the +``doctrine.php`` file a little: + +.. code-block:: php + + setCatchExceptions(true); + $cli->setHelperSet($helperSet); + + // Register All Doctrine Commands + ConsoleRunner::addCommands($cli); + + // Register your own command + $cli->addCommand(new \MyProject\Tools\Console\Commands\MyCustomCommand); + + // Runs console application + $cli->run(); + +Additionally, include multiple commands (and overriding previously +defined ones) is possible through the command: + +.. code-block:: php + + addCommands(array( + new \MyProject\Tools\Console\Commands\MyCustomCommand(), + new \MyProject\Tools\Console\Commands\SomethingCommand(), + new \MyProject\Tools\Console\Commands\AnotherCommand(), + new \MyProject\Tools\Console\Commands\OneMoreCommand(), + )); + + +Re-use console application +-------------------------- + +You are also able to retrieve and re-use the default console application. +Just call ``ConsoleRunner::createApplication(...)`` with an appropriate +HelperSet, like it is described in the configuration section. + +.. code-block:: php + + run(); + diff --git a/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst b/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst new file mode 100644 index 00000000000..4dc18318e62 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst @@ -0,0 +1,350 @@ +Transactions and Concurrency +============================ + +Transaction Demarcation +----------------------- + +Transaction demarcation is the task of defining your transaction +boundaries. Proper transaction demarcation is very important +because if not done properly it can negatively affect the +performance of your application. Many databases and database +abstraction layers like PDO by default operate in auto-commit mode, +which means that every single SQL statement is wrapped in a small +transaction. Without any explicit transaction demarcation from your +side, this quickly results in poor performance because transactions +are not cheap. + +For the most part, Doctrine 2 already takes care of proper +transaction demarcation for you: All the write operations +(INSERT/UPDATE/DELETE) are queued until ``EntityManager#flush()`` +is invoked which wraps all of these changes in a single +transaction. + +However, Doctrine 2 also allows (and encourages) you to take over +and control transaction demarcation yourself. + +These are two ways to deal with transactions when using the +Doctrine ORM and are now described in more detail. + +Approach 1: Implicitly +~~~~~~~~~~~~~~~~~~~~~~ + +The first approach is to use the implicit transaction handling +provided by the Doctrine ORM EntityManager. Given the following +code snippet, without any explicit transaction demarcation: + +.. code-block:: php + + setName('George'); + $em->persist($user); + $em->flush(); + +Since we do not do any custom transaction demarcation in the above +code, ``EntityManager#flush()`` will begin and commit/rollback a +transaction. This behavior is made possible by the aggregation of +the DML operations by the Doctrine ORM and is sufficient if all the +data manipulation that is part of a unit of work happens through +the domain model and thus the ORM. + +Approach 2: Explicitly +~~~~~~~~~~~~~~~~~~~~~~ + +The explicit alternative is to use the ``Doctrine\DBAL\Connection`` +API directly to control the transaction boundaries. The code then +looks like this: + +.. code-block:: php + + getConnection()->beginTransaction(); // suspend auto-commit + try { + //... do some work + $user = new User; + $user->setName('George'); + $em->persist($user); + $em->flush(); + $em->getConnection()->commit(); + } catch (Exception $e) { + $em->getConnection()->rollback(); + throw $e; + } + +Explicit transaction demarcation is required when you want to +include custom DBAL operations in a unit of work or when you want +to make use of some methods of the ``EntityManager`` API that +require an active transaction. Such methods will throw a +``TransactionRequiredException`` to inform you of that +requirement. + +A more convenient alternative for explicit transaction demarcation is the use +of provided control abstractions in the form of +``Connection#transactional($func)`` and ``EntityManager#transactional($func)``. +When used, these control abstractions ensure that you never forget to rollback +the transaction, in addition to the obvious code reduction. An example that is +functionally equivalent to the previously shown code looks as follows: + +.. code-block:: php + + transactional(function($em) { + //... do some work + $user = new User; + $user->setName('George'); + $em->persist($user); + }); + +The difference between ``Connection#transactional($func)`` and +``EntityManager#transactional($func)`` is that the latter +abstraction flushes the ``EntityManager`` prior to transaction +commit and rolls back the transaction when an +exception occurs. + +Exception Handling +~~~~~~~~~~~~~~~~~~ + +When using implicit transaction demarcation and an exception occurs +during ``EntityManager#flush()``, the transaction is automatically +rolled back and the ``EntityManager`` closed. + +When using explicit transaction demarcation and an exception +occurs, the transaction should be rolled back immediately and the +``EntityManager`` closed by invoking ``EntityManager#close()`` and +subsequently discarded, as demonstrated in the example above. This +can be handled elegantly by the control abstractions shown earlier. +Note that when catching ``Exception`` you should generally re-throw +the exception. If you intend to recover from some exceptions, catch +them explicitly in earlier catch blocks (but do not forget to +rollback the transaction and close the ``EntityManager`` there as +well). All other best practices of exception handling apply +similarly (i.e. either log or re-throw, not both, etc.). + +As a result of this procedure, all previously managed or removed +instances of the ``EntityManager`` become detached. The state of +the detached objects will be the state at the point at which the +transaction was rolled back. The state of the objects is in no way +rolled back and thus the objects are now out of synch with the +database. The application can continue to use the detached objects, +knowing that their state is potentially no longer accurate. + +If you intend to start another unit of work after an exception has +occurred you should do that with a new ``EntityManager``. + +Locking Support +--------------- + +Doctrine 2 offers support for Pessimistic- and Optimistic-locking +strategies natively. This allows to take very fine-grained control +over what kind of locking is required for your Entities in your +application. + +Optimistic Locking +~~~~~~~~~~~~~~~~~~ + +Database transactions are fine for concurrency control during a +single request. However, a database transaction should not span +across requests, the so-called "user think time". Therefore a +long-running "business transaction" that spans multiple requests +needs to involve several database transactions. Thus, database +transactions alone can no longer control concurrency during such a +long-running business transaction. Concurrency control becomes the +partial responsibility of the application itself. + +Doctrine has integrated support for automatic optimistic locking +via a version field. In this approach any entity that should be +protected against concurrent modifications during long-running +business transactions gets a version field that is either a simple +number (mapping type: integer) or a timestamp (mapping type: +datetime). When changes to such an entity are persisted at the end +of a long-running conversation the version of the entity is +compared to the version in the database and if they don't match, an +``OptimisticLockException`` is thrown, indicating that the entity +has been modified by someone else already. + +You designate a version field in an entity as follows. In this +example we'll use an integer. + +.. code-block:: php + + find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion); + + // do the work + + $em->flush(); + } catch(OptimisticLockException $e) { + echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; + } + +Or you can use ``EntityManager#lock()`` to find out: + +.. code-block:: php + + find('User', $theEntityId); + + try { + // assert version + $em->lock($entity, LockMode::OPTIMISTIC, $expectedVersion); + + } catch(OptimisticLockException $e) { + echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; + } + +Important Implementation Notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can easily get the optimistic locking workflow wrong if you +compare the wrong versions. Say you have Alice and Bob editing a +hypothetical blog post: + +- Alice reads the headline of the blog post being "Foo", at + optimistic lock version 1 (GET Request) +- Bob reads the headline of the blog post being "Foo", at + optimistic lock version 1 (GET Request) +- Bob updates the headline to "Bar", upgrading the optimistic lock + version to 2 (POST Request of a Form) +- Alice updates the headline to "Baz", ... (POST Request of a + Form) + +Now at the last stage of this scenario the blog post has to be read +again from the database before Alice's headline can be applied. At +this point you will want to check if the blog post is still at +version 1 (which it is not in this scenario). + +Using optimistic locking correctly, you *have* to add the version +as an additional hidden field (or into the SESSION for more +safety). Otherwise you cannot verify the version is still the one +being originally read from the database when Alice performed her +GET request for the blog post. If this happens you might see lost +updates you wanted to prevent with Optimistic Locking. + +See the example code, The form (GET Request): + +.. code-block:: php + + find('BlogPost', 123456); + + echo ''; + echo ''; + +And the change headline action (POST Request): + +.. code-block:: php + + find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion); + +Pessimistic Locking +~~~~~~~~~~~~~~~~~~~ + +Doctrine 2 supports Pessimistic Locking at the database level. No +attempt is being made to implement pessimistic locking inside +Doctrine, rather vendor-specific and ANSI-SQL commands are used to +acquire row-level locks. Every Entity can be part of a pessimistic +lock, there is no special metadata required to use this feature. + +However for Pessimistic Locking to work you have to disable the +Auto-Commit Mode of your Database and start a transaction around +your pessimistic lock use-case using the "Approach 2: Explicit +Transaction Demarcation" described above. Doctrine 2 will throw an +Exception if you attempt to acquire an pessimistic lock and no +transaction is running. + +Doctrine 2 currently supports two pessimistic lock modes: + + +- Pessimistic Write + (``Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE``), locks the + underlying database rows for concurrent Read and Write Operations. +- Pessimistic Read (``Doctrine\DBAL\LockMode::PESSIMISTIC_READ``), + locks other concurrent requests that attempt to update or lock rows + in write mode. + +You can use pessimistic locks in three different scenarios: + + +1. Using + ``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` +2. Using + ``EntityManager#lock($entity, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``EntityManager#lock($entity, \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` +3. Using + ``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` + + diff --git a/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst b/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst new file mode 100644 index 00000000000..da3cc9dc88b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst @@ -0,0 +1,60 @@ +Association Updates: Owning Side and Inverse Side +================================================= + +When mapping bidirectional associations it is important to +understand the concept of the owning and inverse sides. The +following general rules apply: + +- Relationships may be bidirectional or unidirectional. +- A bidirectional relationship has both an owning side and an inverse side +- A unidirectional relationship only has an owning side. +- Doctrine will **only** check the owning side of an association for changes. + +Bidirectional Associations +-------------------------- + +The following rules apply to **bidirectional** associations: + +- The inverse side has to use the ``mappedBy`` attribute of the OneToOne, + OneToMany, or ManyToMany mapping declaration. The mappedBy + attribute contains the name of the association-field on the owning side. +- The owning side has to use the ``inversedBy`` attribute of the + OneToOne, ManyToOne, or ManyToMany mapping declaration. + The inversedBy attribute contains the name of the association-field + on the inverse-side. +- ManyToOne is always the owning side of a bidirectional association. +- OneToMany is always the inverse side of a bidirectional association. +- The owning side of a OneToOne association is the entity with the table + containing the foreign key. +- You can pick the owning side of a many-to-many association yourself. + +Important concepts +------------------ + +**Doctrine will only check the owning side of an association for changes.** + +To fully understand this, remember how bidirectional associations +are maintained in the object world. There are 2 references on each +side of the association and these 2 references both represent the +same association but can change independently of one another. Of +course, in a correct application the semantics of the bidirectional +association are properly maintained by the application developer +(that's his responsibility). Doctrine needs to know which of these +two in-memory references is the one that should be persisted and +which not. This is what the owning/inverse concept is mainly used +for. + +**Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine's point of view)** + +The owning side of a bidirectional association is the side Doctrine +"looks at" when determining the state of the association, and +consequently whether there is anything to do to update the +association in the database. + +.. note:: + + "Owning side" and "inverse side" are technical concepts of + the ORM technology, not concepts of your domain model. What you + consider as the owning side in your domain model can be different + from what the owning side is for Doctrine. These are unrelated. + diff --git a/vendor/doctrine/orm/docs/en/reference/unitofwork.rst b/vendor/doctrine/orm/docs/en/reference/unitofwork.rst new file mode 100644 index 00000000000..cdfb6e0be4a --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/unitofwork.rst @@ -0,0 +1,201 @@ +Doctrine Internals explained +============================ + +Object relational mapping is a complex topic and sufficiently understanding how Doctrine works internally helps you use its full power. + +How Doctrine keeps track of Objects +----------------------------------- + +Doctrine uses the Identity Map pattern to track objects. Whenever you fetch an +object from the database, Doctrine will keep a reference to this object inside +its UnitOfWork. The array holding all the entity references is two-levels deep +and has the keys "root entity name" and "id". Since Doctrine allows composite +keys the id is a sorted, serialized version of all the key columns. + +This allows Doctrine room for optimizations. If you call the EntityManager and +ask for an entity with a specific ID twice, it will return the same instance: + +.. code-block:: php + + public function testIdentityMap() + { + $objectA = $this->entityManager->find('EntityName', 1); + $objectB = $this->entityManager->find('EntityName', 1); + + $this->assertSame($objectA, $objectB) + } + +Only one SELECT query will be fired against the database here. In the second +``EntityManager#find()`` call Doctrine will check the identity map first and +doesn't need to make that database roundtrip. + +Even if you get a proxy object first then fetch the object by the same id you +will still end up with the same reference: + +.. code-block:: php + + public function testIdentityMapReference() + { + $objectA = $this->entityManager->getReference('EntityName', 1); + // check for proxyinterface + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $objectA); + + $objectB = $this->entityManager->find('EntityName', 1); + + $this->assertSame($objectA, $objectB) + } + +The identity map being indexed by primary keys only allows shortcuts when you +ask for objects by primary key. Assume you have the following ``persons`` +table: + +:: + + id | name + ------------- + 1 | Benjamin + 2 | Bud + +Take the following example where two +consecutive calls are made against a repository to fetch an entity by a set of +criteria: + +.. code-block:: php + + public function testIdentityMapRepositoryFindBy() + { + $repository = $this->entityManager->getRepository('Person'); + $objectA = $repository->findOneBy(array('name' => 'Benjamin')); + $objectB = $repository->findOneBy(array('name' => 'Benjamin')); + + $this->assertSame($objectA, $objectB); + } + +This query will still return the same references and `$objectA` and `$objectB` +are indeed referencing the same object. However when checking your SQL logs you +will realize that two queries have been executed against the database. Doctrine +only knows objects by id, so a query for different criteria has to go to the +database, even if it was executed just before. + +But instead of creating a second Person object Doctrine first gets the primary +key from the row and check if it already has an object inside the UnitOfWork +with that primary key. In our example it finds an object and decides to return +this instead of creating a new one. + +The identity map has a second use-case. When you call ``EntityManager#flush`` +Doctrine will ask the identity map for all objects that are currently managed. +This means you don't have to call ``EntityManager#persist`` over and over again +to pass known objects to the EntityManager. This is a NO-OP for known entities, +but leads to much code written that is confusing to other developers. + +The following code WILL update your database with the changes made to the +``Person`` object, even if you did not call ``EntityManager#persist``: + +.. code-block:: php + + find("Person", 1); + $user->setName("Guilherme"); + $entityManager->flush(); + +How Doctrine Detects Changes +---------------------------- + +Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI). +This means you map php objects into a relational database that don't +necessarily know about the database at all. A natural question would now be, +"how does Doctrine even detect objects have changed?". + +For this Doctrine keeps a second map inside the UnitOfWork. Whenever you fetch +an object from the database Doctrine will keep a copy of all the properties and +associations inside the UnitOfWork. Because variables in the PHP language are +subject to "copy-on-write" the memory usage of a PHP request that only reads +objects from the database is the same as if Doctrine did not keep this variable +copy. Only if you start changing variables PHP will create new variables internally +that consume new memory. + +Now whenever you call ``EntityManager#flush`` Doctrine will iterate over the +Identity Map and for each object compares the original property and association +values with the values that are currently set on the object. If changes are +detected then the object is queued for a SQL UPDATE operation. Only the fields +that actually changed are updated. + +This process has an obvious performance impact. The larger the size of the +UnitOfWork is, the longer this computation takes. There are several ways to +optimize the performance of the Flush Operation: + +- Mark entities as read only. These entities can only be inserted or removed, + but are never updated. They are omitted in the changeset calculation. +- Temporarily mark entities as read only. If you have a very large UnitOfWork + but know that a large set of entities has not changed, just mark them as read + only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``. +- Flush only a single entity with ``$entityManager->flush($entity)``. +- Use :doc:`Change Tracking Policies ` to use more + explicit strategies of notifying the UnitOfWork what objects/properties + changed. + + +Query Internals +--------------- + +The different ORM Layers +------------------------ + +Doctrine ships with a set of layers with different responsibilities. This +section gives a short explanation of each layer. + +Hydration +~~~~~~~~~ + +Responsible for creating a final result from a raw database statement and a +result-set mapping object. The developer can choose which kind of result he +wishes to be hydrated. Default result-types include: + +- SQL to Entities +- SQL to structured Arrays +- SQL to simple scalar result arrays +- SQL to a single result variable + +Hydration to entities and arrays is one of most complex parts of Doctrine +algorithm-wise. It can build results with for example: + +- Single table selects +- Joins with n:1 or 1:n cardinality, grouping belonging to the same parent. +- Mixed results of objects and scalar values +- Hydration of results by a given scalar value as key. + +Persisters +~~~~~~~~~~ + +tbr + +UnitOfWork +~~~~~~~~~~ + +tbr + +ResultSetMapping +~~~~~~~~~~~~~~~~ + +tbr + +DQL Parser +~~~~~~~~~~ + +tbr + +SQLWalker +~~~~~~~~~ + +tbr + +EntityManager +~~~~~~~~~~~~~ + +tbr + +ClassMetadataFactory +~~~~~~~~~~~~~~~~~~~~ + +tbr + diff --git a/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst b/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst new file mode 100644 index 00000000000..85ffaee3a2f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst @@ -0,0 +1,712 @@ +Working with Associations +========================= + +Associations between entities are represented just like in regular +object-oriented PHP code using references to other objects or +collections of objects. + +Changes to associations in your code are not synchronized to the +database directly, only when calling ``EntityManager#flush()``. + +There are other concepts you should know about when working +with associations in Doctrine: + +- If an entity is removed from a collection, the association is + removed, not the entity itself. A collection of entities always + only represents the association to the containing entities, not the + entity itself. +- When a bidirectional assocation is updated, Doctrine only checks + on one of both sides for these changes. This is called the :doc:`owning side ` + of the association. +- A property with a reference to many entities has to be instances of the + ``Doctrine\Common\Collections\Collection`` interface. + +Association Example Entities +---------------------------- + +We will use a simple comment system with Users and Comments as +entities to show examples of association management. See the PHP +docblocks of each association in the following example for +information about its type and if it's the owning or inverse side. + +.. code-block:: php + + commentsRead; + } + + public function setFirstComment(Comment $c) { + $this->firstComment = $c; + } + } + +The interaction code would then look like in the following snippet +(``$em`` here is an instance of the EntityManager): + +.. code-block:: php + + find('User', $userId); + + // unidirectional many to many + $comment = $em->find('Comment', $readCommentId); + $user->getReadComments()->add($comment); + + $em->flush(); + + // unidirectional many to one + $myFirstComment = new Comment(); + $user->setFirstComment($myFirstComment); + + $em->persist($myFirstComment); + $em->flush(); + +In the case of bi-directional associations you have to update the +fields on both sides: + +.. code-block:: php + + commentsAuthored; + } + + public function getFavoriteComments() { + return $this->favorites; + } + } + + class Comment + { + // ... + + public function getUserFavorites() { + return $this->userFavorites; + } + + public function setAuthor(User $author = null) { + $this->author = $author; + } + } + + // Many-to-Many + $user->getFavorites()->add($favoriteComment); + $favoriteComment->getUserFavorites()->add($user); + + $em->flush(); + + // Many-To-One / One-To-Many Bidirectional + $newComment = new Comment(); + $user->getAuthoredComments()->add($newComment); + $newComment->setAuthor($user); + + $em->persist($newComment); + $em->flush(); + +Notice how always both sides of the bidirectional association are +updated. The previous unidirectional associations were simpler to +handle. + +Removing Associations +--------------------- + +Removing an association between two entities is similarly +straight-forward. There are two strategies to do so, by key and by +element. Here are some examples: + +.. code-block:: php + + getComments()->removeElement($comment); + $comment->setAuthor(null); + + $user->getFavorites()->removeElement($comment); + $comment->getUserFavorites()->removeElement($user); + + // Remove by Key + $user->getComments()->remove($ithComment); + $comment->setAuthor(null); + +You need to call ``$em->flush()`` to make persist these changes in +the database permanently. + +Notice how both sides of the bidirectional association are always +updated. Unidirectional associations are consequently simpler to +handle. Also note that if you use type-hinting in your methods, i.e. +``setAddress(Address $address)``, PHP will only allow null +values if ``null`` is set as default value. Otherwise +setAddress(null) will fail for removing the association. If you +insist on type-hinting a typical way to deal with this is to +provide a special method, like ``removeAddress()``. This can also +provide better encapsulation as it hides the internal meaning of +not having an address. + +When working with collections, keep in mind that a Collection is +essentially an ordered map (just like a PHP array). That is why the +``remove`` operation accepts an index/key. ``removeElement`` is a +separate method that has O(n) complexity using ``array_search``, +where n is the size of the map. + +.. note:: + + Since Doctrine always only looks at the owning side of a + bidirectional association for updates, it is not necessary for + write operations that an inverse collection of a bidirectional + one-to-many or many-to-many association is updated. This knowledge + can often be used to improve performance by avoiding the loading of + the inverse collection. + + +You can also clear the contents of a whole collection using the +``Collections::clear()`` method. You should be aware that using +this method can lead to a straight and optimized database delete or +update call during the flush operation that is not aware of +entities that have been re-added to the collection. + +Say you clear a collection of tags by calling +``$post->getTags()->clear();`` and then call +``$post->getTags()->add($tag)``. This will not recognize the tag having +already been added previously and will consequently issue two separate database +calls. + +Association Management Methods +------------------------------ + +It is generally a good idea to encapsulate proper association +management inside the entity classes. This makes it easier to use +the class correctly and can encapsulate details about how the +association is maintained. + +The following code shows updates to the previous User and Comment +example that encapsulate much of the association management code: + +.. code-block:: php + + commentsRead[] = $comment; + } + + public function addComment(Comment $comment) { + if (count($this->commentsAuthored) == 0) { + $this->setFirstComment($comment); + } + $this->comments[] = $comment; + $comment->setAuthor($this); + } + + private function setFirstComment(Comment $c) { + $this->firstComment = $c; + } + + public function addFavorite(Comment $comment) { + $this->favorites->add($comment); + $comment->addUserFavorite($this); + } + + public function removeFavorite(Comment $comment) { + $this->favorites->removeElement($comment); + $comment->removeUserFavorite($this); + } + } + + class Comment + { + // .. + + public function addUserFavorite(User $user) { + $this->userFavorites[] = $user; + } + + public function removeUserFavorite(User $user) { + $this->userFavorites->removeElement($user); + } + } + +You will notice that ``addUserFavorite`` and ``removeUserFavorite`` +do not call ``addFavorite`` and ``removeFavorite``, thus the +bidirectional association is strictly-speaking still incomplete. +However if you would naively add the ``addFavorite`` in +``addUserFavorite``, you end up with an infinite loop, so more work +is needed. As you can see, proper bidirectional association +management in plain OOP is a non-trivial task and encapsulating all +the details inside the classes can be challenging. + +.. note:: + + If you want to make sure that your collections are perfectly + encapsulated you should not return them from a + ``getCollectionName()`` method directly, but call + ``$collection->toArray()``. This way a client programmer for the + entity cannot circumvent the logic you implement on your entity for + association management. For example: + + +.. code-block:: php + + commentsRead->toArray(); + } + } + +This will however always initialize the collection, with all the +performance penalties given the size. In some scenarios of large +collections it might even be a good idea to completely hide the +read access behind methods on the EntityRepository. + +There is no single, best way for association management. It greatly +depends on the requirements of your concrete domain model as well +as your preferences. + +Synchronizing Bidirectional Collections +--------------------------------------- + +In the case of Many-To-Many associations you as the developer have the +responsibility of keeping the collections on the owning and inverse side +in sync when you apply changes to them. Doctrine can only +guarantee a consistent state for the hydration, not for your client +code. + +Using the User-Comment entities from above, a very simple example +can show the possible caveats you can encounter: + +.. code-block:: php + + getFavorites()->add($favoriteComment); + // not calling $favoriteComment->getUserFavorites()->add($user); + + $user->getFavorites()->contains($favoriteComment); // TRUE + $favoriteComment->getUserFavorites()->contains($user); // FALSE + +There are two approaches to handle this problem in your code: + + +1. Ignore updating the inverse side of bidirectional collections, + BUT never read from them in requests that changed their state. In + the next Request Doctrine hydrates the consistent collection state + again. +2. Always keep the bidirectional collections in sync through + association management methods. Reads of the Collections directly + after changes are consistent then. + +Transitive persistence / Cascade Operations +------------------------------------------- + +Persisting, removing, detaching, refreshing and merging individual entities can +become pretty cumbersome, especially when a highly interweaved object graph +is involved. Therefore Doctrine 2 provides a +mechanism for transitive persistence through cascading of these +operations. Each association to another entity or a collection of +entities can be configured to automatically cascade certain +operations. By default, no operations are cascaded. + +The following cascade options exist: + + +- persist : Cascades persist operations to the associated + entities. +- remove : Cascades remove operations to the associated entities. +- merge : Cascades merge operations to the associated entities. +- detach : Cascades detach operations to the associated entities. +- refresh : Cascades refresh operations to the associated entities. +- all : Cascades persist, remove, merge, refresh and detach operations to + associated entities. + +.. note:: + + Cascade operations are performed in memory. That means collections and related entities + are fetched into memory, even if they are still marked as lazy when + the cascade operation is about to be performed. However this approach allows + entity lifecycle events to be performed for each of these operations. + + However, pulling objects graph into memory on cascade can cause considerable performance + overhead, especially when cascading collections are large. Makes sure + to weigh the benefits and downsides of each cascade operation that you define. + + To rely on the database level cascade operations for the delete operation instead, you can + configure each join column with the **onDelete** option. See the respective + mapping driver chapters for more information. + +The following example is an extension to the User-Comment example +of this chapter. Suppose in our application a user is created +whenever he writes his first comment. In this case we would use the +following code: + +.. code-block:: php + + addComment($myFirstComment); + + $em->persist($user); + $em->persist($myFirstComment); + $em->flush(); + +Even if you *persist* a new User that contains our new Comment this +code would fail if you removed the call to +``EntityManager#persist($myFirstComment)``. Doctrine 2 does not +cascade the persist operation to all nested entities that are new +as well. + +More complicated is the deletion of all of a user's comments when he is +removed from the system: + +.. code-block:: php + + find('User', $deleteUserId); + + foreach ($user->getAuthoredComments() as $comment) { + $em->remove($comment); + } + $em->remove($user); + $em->flush(); + +Without the loop over all the authored comments Doctrine would use +an UPDATE statement only to set the foreign key to NULL and only +the User would be deleted from the database during the +flush()-Operation. + +To have Doctrine handle both cases automatically we can change the +``User#commentsAuthored`` property to cascade both the "persist" +and the "remove" operation. + +.. code-block:: php + + addresses = new ArrayCollection(); + } + + public function newStandingData(StandingData $sd) + { + $this->standingData = $sd; + } + + public function removeAddress($pos) + { + unset($this->addresses[$pos]); + } + } + +Now two examples of what happens when you remove the references: + +.. code-block:: php + + find("Addressbook\Contact", $contactId); + $contact->newStandingData(new StandingData("Firstname", "Lastname", "Street")); + $contact->removeAddress(1); + + $em->flush(); + +In this case you have not only changed the ``Contact`` entity itself but +you have also removed the references for standing data and as well as one +address reference. When flush is called not only are the references removed +but both the old standing data and the one address entity are also deleted +from the database. + +Filtering Collections +--------------------- + +.. filtering-collections: + +Collections have a filtering API that allows to slice parts of data from +a collection. If the collection has not been loaded from the database yet, +the filtering API can work on the SQL level to make optimized access to +large collections. + +.. code-block:: php + + find('Group', $groupId); + $userCollection = $group->getUsers(); + + $criteria = Criteria::create() + ->where(Criteria::expr()->eq("birthday", "1982-02-17")) + ->orderBy(array("username" => Criteria::ASC)) + ->setFirstResult(0) + ->setMaxResults(20) + ; + + $birthdayUsers = $userCollection->matching($criteria); + +.. tip:: + + You can move the access of slices of collections into dedicated methods of + an entity. For example ``Group#getTodaysBirthdayUsers()``. + +The Criteria has a limited matching language that works both on the +SQL and on the PHP collection level. This means you can use collection matching +interchangeably, independent of in-memory or sql-backed collections. + +.. code-block:: php + + find('CMS\Article', 1234); + $article->setHeadline('Hello World dude!'); + + $article2 = $entityManager->find('CMS\Article', 1234); + echo $article2->getHeadline(); + +In this case the Article is accessed from the entity manager twice, +but modified in between. Doctrine 2 realizes this and will only +ever give you access to one instance of the Article with ID 1234, +no matter how often do you retrieve it from the EntityManager and +even no matter what kind of Query method you are using (find, +Repository Finder or DQL). This is called "Identity Map" pattern, +which means Doctrine keeps a map of each entity and ids that have +been retrieved per PHP request and keeps returning you the same +instances. + +In the previous example the echo prints "Hello World dude!" to the +screen. You can even verify that ``$article`` and ``$article2`` are +indeed pointing to the same instance by running the following +code: + +.. code-block:: php + + comments = new ArrayCollection(); + } + + public function getAuthor() { return $this->author; } + public function getComments() { return $this->comments; } + } + + $article = $em->find('Article', 1); + +This code only retrieves the ``Article`` instance with id 1 executing +a single SELECT statement against the articles table in the database. +You can still access the associated properties author and comments +and the associated objects they contain. + +This works by utilizing the lazy loading pattern. Instead of +passing you back a real Author instance and a collection of +comments Doctrine will create proxy instances for you. Only if you +access these proxies for the first time they will go through the +EntityManager and load their state from the database. + +This lazy-loading process happens behind the scenes, hidden from +your code. See the following code: + +.. code-block:: php + + find('Article', 1); + + // accessing a method of the user instance triggers the lazy-load + echo "Author: " . $article->getAuthor()->getName() . "\n"; + + // Lazy Loading Proxies pass instanceof tests: + if ($article->getAuthor() instanceof User) { + // a User Proxy is a generated "UserProxy" class + } + + // accessing the comments as an iterator triggers the lazy-load + // retrieving ALL the comments of this article from the database + // using a single SELECT statement + foreach ($article->getComments() as $comment) { + echo $comment->getText() . "\n\n"; + } + + // Article::$comments passes instanceof tests for the Collection interface + // But it will NOT pass for the ArrayCollection interface + if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) { + echo "This will always be true!"; + } + +A slice of the generated proxy classes code looks like the +following piece of code. A real proxy class override ALL public +methods along the lines of the ``getName()`` method shown below: + +.. code-block:: php + + _load(); + return parent::getName(); + } + // .. other public methods of User + } + +.. warning:: + + Traversing the object graph for parts that are lazy-loaded will + easily trigger lots of SQL queries and will perform badly if used + to heavily. Make sure to use DQL to fetch-join all the parts of the + object-graph that you need as efficiently as possible. + + +Persisting entities +------------------- + +An entity can be made persistent by passing it to the +``EntityManager#persist($entity)`` method. By applying the persist +operation on some entity, that entity becomes MANAGED, which means +that its persistence is from now on managed by an EntityManager. As +a result the persistent state of such an entity will subsequently +be properly synchronized with the database when +``EntityManager#flush()`` is invoked. + +.. note:: + + Invoking the ``persist`` method on an entity does NOT + cause an immediate SQL INSERT to be issued on the database. + Doctrine applies a strategy called "transactional write-behind", + which means that it will delay most SQL commands until + ``EntityManager#flush()`` is invoked which will then issue all + necessary SQL statements to synchronize your objects with the + database in the most efficient way and a single, short transaction, + taking care of maintaining referential integrity. + + +Example: + +.. code-block:: php + + setName('Mr.Right'); + $em->persist($user); + $em->flush(); + +.. note:: + + Generated entity identifiers / primary keys are + guaranteed to be available after the next successful flush + operation that involves the entity in question. You can not rely on + a generated identifier to be available directly after invoking + ``persist``. The inverse is also true. You can not rely on a + generated identifier being not available after a failed flush + operation. + + +The semantics of the persist operation, applied on an entity X, are +as follows: + + +- If X is a new entity, it becomes managed. The entity X will be + entered into the database as a result of the flush operation. +- If X is a preexisting managed entity, it is ignored by the + persist operation. However, the persist operation is cascaded to + entities referenced by X, if the relationships from X to these + other entities are mapped with cascade=PERSIST or cascade=ALL (see + "Transitive Persistence"). +- If X is a removed entity, it becomes managed. +- If X is a detached entity, an exception will be thrown on + flush. + +Removing entities +----------------- + +An entity can be removed from persistent storage by passing it to +the ``EntityManager#remove($entity)`` method. By applying the +``remove`` operation on some entity, that entity becomes REMOVED, +which means that its persistent state will be deleted once +``EntityManager#flush()`` is invoked. + +.. note:: + + Just like ``persist``, invoking ``remove`` on an entity + does NOT cause an immediate SQL DELETE to be issued on the + database. The entity will be deleted on the next invocation of + ``EntityManager#flush()`` that involves that entity. This + means that entities scheduled for removal can still be queried + for and appear in query and collection results. See + the section on :ref:`Database and UnitOfWork Out-Of-Sync ` + for more information. + + +Example: + +.. code-block:: php + + remove($user); + $em->flush(); + +The semantics of the remove operation, applied to an entity X are +as follows: + + +- If X is a new entity, it is ignored by the remove operation. + However, the remove operation is cascaded to entities referenced by + X, if the relationship from X to these other entities is mapped + with cascade=REMOVE or cascade=ALL (see "Transitive Persistence"). +- If X is a managed entity, the remove operation causes it to + become removed. The remove operation is cascaded to entities + referenced by X, if the relationships from X to these other + entities is mapped with cascade=REMOVE or cascade=ALL (see + "Transitive Persistence"). +- If X is a detached entity, an InvalidArgumentException will be + thrown. +- If X is a removed entity, it is ignored by the remove operation. +- A removed entity X will be removed from the database as a result + of the flush operation. + +After an entity has been removed its in-memory state is the same as +before the removal, except for generated identifiers. + +Removing an entity will also automatically delete any existing +records in many-to-many join tables that link this entity. The +action taken depends on the value of the ``@joinColumn`` mapping +attribute "onDelete". Either Doctrine issues a dedicated ``DELETE`` +statement for records of each join table or it depends on the +foreign key semantics of onDelete="CASCADE". + +Deleting an object with all its associated objects can be achieved +in multiple ways with very different performance impacts. + + +1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2 + will fetch this association. If its a Single association it will + pass this entity to + ´EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()\`. + In both cases the cascade remove semantics are applied recursively. + For large object graphs this removal strategy can be very costly. +2. Using a DQL ``DELETE`` statement allows you to delete multiple + entities of a type with a single command and without hydrating + these entities. This can be very efficient to delete large object + graphs from the database. +3. Using foreign key semantics ``onDelete="CASCADE"`` can force the + database to remove all associated objects internally. This strategy + is a bit tricky to get right but can be very powerful and fast. You + should be aware however that using strategy 1 (``CASCADE=REMOVE``) + completely by-passes any foreign key ``onDelete=CASCADE`` option, + because Doctrine will fetch and remove all associated entities + explicitly nevertheless. + +Detaching entities +------------------ + +An entity is detached from an EntityManager and thus no longer +managed by invoking the ``EntityManager#detach($entity)`` method on +it or by cascading the detach operation to it. Changes made to the +detached entity, if any (including removal of the entity), will not +be synchronized to the database after the entity has been +detached. + +Doctrine will not hold on to any references to a detached entity. + +Example: + +.. code-block:: php + + detach($entity); + +The semantics of the detach operation, applied to an entity X are +as follows: + + +- If X is a managed entity, the detach operation causes it to + become detached. The detach operation is cascaded to entities + referenced by X, if the relationships from X to these other + entities is mapped with cascade=DETACH or cascade=ALL (see + "Transitive Persistence"). Entities which previously referenced X + will continue to reference X. +- If X is a new or detached entity, it is ignored by the detach + operation. +- If X is a removed entity, the detach operation is cascaded to + entities referenced by X, if the relationships from X to these + other entities is mapped with cascade=DETACH or cascade=ALL (see + "Transitive Persistence"). Entities which previously referenced X + will continue to reference X. + +There are several situations in which an entity is detached +automatically without invoking the ``detach`` method: + + +- When ``EntityManager#clear()`` is invoked, all entities that are + currently managed by the EntityManager instance become detached. +- When serializing an entity. The entity retrieved upon subsequent + unserialization will be detached (This is the case for all entities + that are serialized and stored in some cache, i.e. when using the + Query Result Cache). + +The ``detach`` operation is usually not as frequently needed and +used as ``persist`` and ``remove``. + +Merging entities +---------------- + +Merging entities refers to the merging of (usually detached) +entities into the context of an EntityManager so that they become +managed again. To merge the state of an entity into an +EntityManager use the ``EntityManager#merge($entity)`` method. The +state of the passed entity will be merged into a managed copy of +this entity and this copy will subsequently be returned. + +Example: + +.. code-block:: php + + merge($detachedEntity); + // $entity now refers to the fully managed copy returned by the merge operation. + // The EntityManager $em now manages the persistence of $entity as usual. + +.. note:: + + When you want to serialize/unserialize entities you + have to make all entity properties protected, never private. The + reason for this is, if you serialize a class that was a proxy + instance before, the private variables won't be serialized and a + PHP Notice is thrown. + + +The semantics of the merge operation, applied to an entity X, are +as follows: + + +- If X is a detached entity, the state of X is copied onto a + pre-existing managed entity instance X' of the same identity. +- If X is a new entity instance, a new managed copy X' will be + created and the state of X is copied onto this managed instance. +- If X is a removed entity instance, an InvalidArgumentException + will be thrown. +- If X is a managed entity, it is ignored by the merge operation, + however, the merge operation is cascaded to entities referenced by + relationships from X if these relationships have been mapped with + the cascade element value MERGE or ALL (see "Transitive + Persistence"). +- For all entities Y referenced by relationships from X having the + cascade element value MERGE or ALL, Y is merged recursively as Y'. + For all such Y referenced by X, X' is set to reference Y'. (Note + that if X is managed then X is the same object as X'.) +- If X is an entity merged to X', with a reference to another + entity Y, where cascade=MERGE or cascade=ALL is not specified, then + navigation of the same association from X' yields a reference to a + managed object Y' with the same persistent identity as Y. + +The ``merge`` operation will throw an ``OptimisticLockException`` +if the entity being merged uses optimistic locking through a +version field and the versions of the entity being merged and the +managed copy don't match. This usually means that the entity has +been modified while being detached. + +The ``merge`` operation is usually not as frequently needed and +used as ``persist`` and ``remove``. The most common scenario for +the ``merge`` operation is to reattach entities to an EntityManager +that come from some cache (and are therefore detached) and you want +to modify and persist such an entity. + +.. warning:: + + If you need to perform multiple merges of entities that share certain subparts + of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the + successive calls to ``EntityManager#merge()``. Otherwise you might end up with + multiple copies of the "same" object in the database, however with different ids. + +.. note:: + + If you load some detached entities from a cache and you do + not need to persist or delete them or otherwise make use of them + without the need for persistence services there is no need to use + ``merge``. I.e. you can simply pass detached objects from a cache + directly to the view. + + +Synchronization with the Database +--------------------------------- + +The state of persistent entities is synchronized with the database +on flush of an ``EntityManager`` which commits the underlying +``UnitOfWork``. The synchronization involves writing any updates to +persistent entities and their relationships to the database. +Thereby bidirectional relationships are persisted based on the +references held by the owning side of the relationship as explained +in the Association Mapping chapter. + +When ``EntityManager#flush()`` is called, Doctrine inspects all +managed, new and removed entities and will perform the following +operations. + +.. _workingobjects_database_uow_outofsync: + +Effects of Database and UnitOfWork being Out-Of-Sync +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As soon as you begin to change the state of entities, call persist or remove the +contents of the UnitOfWork and the database will drive out of sync. They can +only be synchronized by calling ``EntityManager#flush()``. This section +describes the effects of database and UnitOfWork being out of sync. + +- Entities that are scheduled for removal can still be queried from the database. + They are returned from DQL and Repository queries and are visible in collections. +- Entities that are passed to ``EntityManager#persist`` do not turn up in query + results. +- Entities that have changed will not be overwritten with the state from the database. + This is because the identity map will detect the construction of an already existing + entity and assumes its the most up to date version. + +``EntityManager#flush()`` is never called implicitly by Doctrine. You always have to trigger it manually. + +Synchronizing New and Managed Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The flush operation applies to a managed entity with the following +semantics: + + +- The entity itself is synchronized to the database using a SQL + UPDATE statement, only if at least one persistent field has + changed. +- No SQL updates are executed if the entity did not change. + +The flush operation applies to a new entity with the following +semantics: + + +- The entity itself is synchronized to the database using a SQL + INSERT statement. + +For all (initialized) relationships of the new or managed entity +the following semantics apply to each associated entity X: + + +- If X is new and persist operations are configured to cascade on + the relationship, X will be persisted. +- If X is new and no persist operations are configured to cascade + on the relationship, an exception will be thrown as this indicates + a programming error. +- If X is removed and persist operations are configured to cascade + on the relationship, an exception will be thrown as this indicates + a programming error (X would be re-persisted by the cascade). +- If X is detached and persist operations are configured to + cascade on the relationship, an exception will be thrown (This is + semantically the same as passing X to persist()). + +Synchronizing Removed Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The flush operation applies to a removed entity by deleting its +persistent state from the database. No cascade options are relevant +for removed entities on flush, the cascade remove option is already +executed during ``EntityManager#remove($entity)``. + +The size of a Unit of Work +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The size of a Unit of Work mainly refers to the number of managed +entities at a particular point in time. + +The cost of flushing +~~~~~~~~~~~~~~~~~~~~ + +How costly a flush operation is, mainly depends on two factors: + + +- The size of the EntityManager's current UnitOfWork. +- The configured change tracking policies + +You can get the size of a UnitOfWork as follows: + +.. code-block:: php + + getUnitOfWork()->size(); + +The size represents the number of managed entities in the Unit of +Work. This size affects the performance of flush() operations due +to change tracking (see "Change Tracking Policies") and, of course, +memory consumption, so you may want to check it from time to time +during development. + +.. note:: + + Do not invoke ``flush`` after every change to an entity + or every single invocation of persist/remove/merge/... This is an + anti-pattern and unnecessarily reduces the performance of your + application. Instead, form units of work that operate on your + objects and call ``flush`` when you are done. While serving a + single HTTP request there should be usually no need for invoking + ``flush`` more than 0-2 times. + + +Direct access to a Unit of Work +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get direct access to the Unit of Work by calling +``EntityManager#getUnitOfWork()``. This will return the UnitOfWork +instance the EntityManager is currently using. + +.. code-block:: php + + getUnitOfWork(); + +.. note:: + + Directly manipulating a UnitOfWork is not recommended. + When working directly with the UnitOfWork API, respect methods + marked as INTERNAL by not using them and carefully read the API + documentation. + + +Entity State +~~~~~~~~~~~~ + +As outlined in the architecture overview an entity can be in one of +four possible states: NEW, MANAGED, REMOVED, DETACHED. If you +explicitly need to find out what the current state of an entity is +in the context of a certain ``EntityManager`` you can ask the +underlying ``UnitOfWork``: + +.. code-block:: php + + getUnitOfWork()->getEntityState($entity)) { + case UnitOfWork::STATE_MANAGED: + ... + case UnitOfWork::STATE_REMOVED: + ... + case UnitOfWork::STATE_DETACHED: + ... + case UnitOfWork::STATE_NEW: + ... + } + +An entity is in MANAGED state if it is associated with an +``EntityManager`` and it is not REMOVED. + +An entity is in REMOVED state after it has been passed to +``EntityManager#remove()`` until the next flush operation of the +same EntityManager. A REMOVED entity is still associated with an +``EntityManager`` until the next flush operation. + +An entity is in DETACHED state if it has persistent state and +identity but is currently not associated with an +``EntityManager``. + +An entity is in NEW state if has no persistent state and identity +and is not associated with an ``EntityManager`` (for example those +just created via the "new" operator). + +Querying +-------- + +Doctrine 2 provides the following ways, in increasing level of +power and flexibility, to query for persistent objects. You should +always start with the simplest one that suits your needs. + +By Primary Key +~~~~~~~~~~~~~~ + +The most basic way to query for a persistent object is by its +identifier / primary key using the +``EntityManager#find($entityName, $id)`` method. Here is an +example: + +.. code-block:: php + + find('MyProject\Domain\User', $id); + +The return value is either the found entity instance or null if no +instance could be found with the given identifier. + +Essentially, ``EntityManager#find()`` is just a shortcut for the +following: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->find($id); + +``EntityManager#getRepository($entityName)`` returns a repository +object which provides many ways to retrieve entities of the +specified type. By default, the repository instance is of type +``Doctrine\ORM\EntityRepository``. You can also use custom +repository classes as shown later. + +By Simple Conditions +~~~~~~~~~~~~~~~~~~~~ + +To query for one or more entities based on several conditions that +form a logical conjunction, use the ``findBy`` and ``findOneBy`` +methods on a repository as follows: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => 20)); + + // All users that are 20 years old and have a surname of 'Miller' + $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller')); + + // A single user by its nickname + $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb')); + +You can also load by owning side associations through the repository: + +.. code-block:: php + + find('MyProject\Domain\Phonenumber', 1234); + $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId())); + +Be careful that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself. + +The ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0); + +If you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => array(20, 30, 40))); + // translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40) + +An EntityRepository also provides a mechanism for more concise +calls through its use of ``__call``. Thus, the following two +examples are equivalent: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb')); + + // A single user by its nickname (__call magic) + $user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb'); + +By Criteria +~~~~~~~~~~~ + +.. versionadded:: 2.3 + +The Repository implement the ``Doctrine\Common\Collections\Selectable`` +interface. That means you can build ``Doctrine\Common\Collections\Criteria`` +and pass them to the ``matching($criteria)`` method. + +See the :ref:`Working with Associations: Filtering collections +`. + +By Eager Loading +~~~~~~~~~~~~~~~~ + +Whenever you query for an entity that has persistent associations +and these associations are mapped as EAGER, they will automatically +be loaded together with the entity being queried and is thus +immediately available to your application. + +By Lazy Loading +~~~~~~~~~~~~~~~ + +Whenever you have a managed entity instance at hand, you can +traverse and use any associations of that entity that are +configured LAZY as if they were in-memory already. Doctrine will +automatically load the associated objects on demand through the +concept of lazy-loading. + +By DQL +~~~~~~ + +The most powerful and flexible method to query for persistent +objects is the Doctrine Query Language, an object query language. +DQL enables you to query for persistent objects in the language of +objects. DQL understands classes, fields, inheritance and +associations. DQL is syntactically very similar to the familiar SQL +but *it is not SQL*. + +A DQL query is represented by an instance of the +``Doctrine\ORM\Query`` class. You create a query using +``EntityManager#createQuery($dql)``. Here is a simple example: + +.. code-block:: php + + createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30"); + $users = $q->getResult(); + +Note that this query contains no knowledge about the relational +schema, only about the object model. DQL supports positional as +well as named parameters, many functions, (fetch) joins, +aggregates, subqueries and much more. Detailed information about +DQL and its syntax as well as the Doctrine class can be found in +:doc:`the dedicated chapter `. +For programmatically building up queries based on conditions that +are only known at runtime, Doctrine provides the special +``Doctrine\ORM\QueryBuilder`` class. More information on +constructing queries with a QueryBuilder can be found +:doc:`in Query Builder chapter `. + +By Native Queries +~~~~~~~~~~~~~~~~~ + +As an alternative to DQL or as a fallback for special SQL +statements native queries can be used. Native queries are built by +using a hand-crafted SQL query and a ResultSetMapping that +describes how the SQL result set should be transformed by Doctrine. +More information about native queries can be found in +:doc:`the dedicated chapter `. + +Custom Repositories +~~~~~~~~~~~~~~~~~~~ + +By default the EntityManager returns a default implementation of +``Doctrine\ORM\EntityRepository`` when you call +``EntityManager#getRepository($entityClass)``. You can overwrite +this behaviour by specifying the class name of your own Entity +Repository in the Annotation, XML or YAML metadata. In large +applications that require lots of specialized DQL queries using a +custom repository is one recommended way of grouping these queries +in a central location. + +.. code-block:: php + + _em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"') + ->getResult(); + } + } + +You can access your repository now by calling: + +.. code-block:: php + + getRepository('MyDomain\Model\User')->getAllAdminUsers(); + + diff --git a/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst b/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst new file mode 100644 index 00000000000..dbf97dc0ad1 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst @@ -0,0 +1,782 @@ +XML Mapping +=========== + +The XML mapping driver enables you to provide the ORM metadata in +form of XML documents. + +The XML driver is backed by an XML Schema document that describes +the structure of a mapping document. The most recent version of the +XML Schema document is available online at +`http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd `_. +In order to point to the latest version of the document of a +particular stable release branch, just append the release number, +i.e.: doctrine-mapping-2.0.xsd The most convenient way to work with +XML mapping files is to use an IDE/editor that can provide +code-completion based on such an XML Schema document. The following +is an outline of a XML mapping document with the proper xmlns/xsi +setup for the latest code in trunk. + +.. code-block:: xml + + + + ... + + + +The XML mapping document of a class is loaded on-demand the first +time it is requested and subsequently stored in the metadata cache. +In order to work, this requires certain conventions: + + +- Each entity/mapped superclass must get its own dedicated XML + mapping document. +- The name of the mapping document must consist of the fully + qualified name of the class, where namespace separators are + replaced by dots (.). For example an Entity with the fully + qualified class-name "MyProject" would require a mapping file + "MyProject.Entities.User.dcm.xml" unless the extension is changed. +- All mapping documents should get the extension ".dcm.xml" to + identify it as a Doctrine mapping file. This is more of a + convention and you are not forced to do this. You can change the + file extension easily enough. + +.. code-block:: php + + setFileExtension('.xml'); + +It is recommended to put all XML mapping documents in a single +folder but you can spread the documents over several folders if you +want to. In order to tell the XmlDriver where to look for your +mapping documents, supply an array of paths as the first argument +of the constructor, like this: + +.. code-block:: php + + setMetadataDriverImpl($driver); + +.. warning:: + + Note that Doctrine ORM does not modify any settings for ``libxml``, + therefore, external XML entities may or may not be enabled or + configured correctly. + XML mappings are not XXE/XEE attack vectors since they are not + related with user input, but it is recommended that you do not + use external XML entities in your mapping files to avoid running + into unexpected behaviour. + +Simplified XML Driver +~~~~~~~~~~~~~~~~~~~~~ + +The Symfony project sponsored a driver that simplifies usage of the XML Driver. +The changes between the original driver are: + +1. File Extension is .orm.xml +2. Filenames are shortened, "MyProject\Entities\User" will become User.orm.xml +3. You can add a global file and add multiple entities in this file. + +Configuration of this client works a little bit different: + +.. code-block:: php + + 'MyProject\Entities', + '/path/to/files2' => 'OtherProject\Entities' + ); + $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver($namespaces); + $driver->setGlobalBasename('global'); // global.orm.xml + +Example +------- + +As a quick start, here is a small example document that makes use +of several common elements: + +.. code-block:: xml + + // Doctrine.Tests.ORM.Mapping.User.dcm.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Be aware that class-names specified in the XML files should be +fully qualified. + +XML-Element Reference +--------------------- + +The XML-Element reference explains all the tags and attributes that +the Doctrine Mapping XSD Schema defines. You should read the +Basic-, Association- and Inheritance Mapping chapters to understand +what each of this definitions means in detail. + +Defining an Entity +~~~~~~~~~~~~~~~~~~ + +Each XML Mapping File contains the definition of one entity, +specified as the ```` element as a direct child of the +```` element: + +.. code-block:: xml + + + + + + + +Required attributes: + + +- name - The fully qualified class-name of the entity. + +Optional attributes: + + +- **table** - The Table-Name to be used for this entity. Otherwise the + Unqualified Class-Name is used by default. +- **repository-class** - The fully qualified class-name of an + alternative ``Doctrine\ORM\EntityRepository`` implementation to be + used with this entity. +- **inheritance-type** - The type of inheritance, defaults to none. A + more detailed description follows in the + *Defining Inheritance Mappings* section. +- **read-only** - (>= 2.1) Specifies that this entity is marked as read only and not + considered for change-tracking. Entities of this type can be persisted + and removed though. +- **schema** - (>= 2.5) The schema the table lies in, for platforms that support schemas + +Defining Fields +~~~~~~~~~~~~~~~ + +Each entity class can contain zero to infinite fields that are +managed by Doctrine. You can define them using the ```` +element as a children to the ```` element. The field +element is only used for primitive types that are not the ID of the +entity. For the ID mapping you have to use the ```` element. + +.. code-block:: xml + + + + + + + + + + + + + + + + +Required attributes: + + +- name - The name of the Property/Field on the given Entity PHP + class. + +Optional attributes: + + +- type - The ``Doctrine\DBAL\Types\Type`` name, defaults to + "string" +- column - Name of the column in the database, defaults to the + field name. +- length - The length of the given type, for use with strings + only. +- unique - Should this field contain a unique value across the + table? Defaults to false. +- nullable - Should this field allow NULL as a value? Defaults to + false. +- version - Should this field be used for optimistic locking? Only + works on fields with type integer or datetime. +- scale - Scale of a decimal type. +- precision - Precision of a decimal type. +- options - Array of additional options: + + - default - The default value to set for the column if no value + is supplied. + - unsigned - Boolean value to determine if the column should + be capable of representing only non-negative integers + (applies only for integer column and might not be supported by + all vendors). + - fixed - Boolean value to determine if the specified length of + a string column should be fixed or varying (applies only for + string/binary column and might not be supported by all vendors). + - comment - The comment of the column in the schema (might not + be supported by all vendors). + - customSchemaOptions - Array of additional schema options + which are mostly vendor specific. +- column-definition - Optional alternative SQL representation for + this column. This definition begin after the field-name and has to + specify the complete column definition. Using this feature will + turn this field dirty for Schema-Tool update commands at all + times. + +.. note:: + + For more detailed information on each attribute, please refer to + the DBAL ``Schema-Representation`` documentation. + +Defining Identity and Generator Strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An entity has to have at least one ```` element. For +composite keys you can specify more than one id-element, however +surrogate keys are recommended for use with Doctrine 2. The Id +field allows to define properties of the identifier and allows a +subset of the ```` element attributes: + +.. code-block:: xml + + + + + +Required attributes: + + +- name - The name of the Property/Field on the given Entity PHP + class. +- type - The ``Doctrine\DBAL\Types\Type`` name, preferably + "string" or "integer". + +Optional attributes: + + +- column - Name of the column in the database, defaults to the + field name. + +Using the simplified definition above Doctrine will use no +identifier strategy for this entity. That means you have to +manually set the identifier before calling +``EntityManager#persist($entity)``. This is the so called +``ASSIGNED`` strategy. + +If you want to switch the identifier generation strategy you have +to nest a ```` element inside the id-element. This of +course only works for surrogate keys. For composite keys you always +have to use the ``ASSIGNED`` strategy. + +.. code-block:: xml + + + + + + + +The following values are allowed for the ```` strategy +attribute: + + +- AUTO - Automatic detection of the identifier strategy based on + the preferred solution of the database vendor. +- IDENTITY - Use of a IDENTIFY strategy such as Auto-Increment IDs + available to Doctrine AFTER the INSERT statement has been executed. +- SEQUENCE - Use of a database sequence to retrieve the + entity-ids. This is possible before the INSERT statement is + executed. + +If you are using the SEQUENCE strategy you can define an additional +element to describe the sequence: + +.. code-block:: xml + + + + + + + + +Required attributes for ````: + + +- sequence-name - The name of the sequence + +Optional attributes for ````: + + +- allocation-size - By how much steps should the sequence be + incremented when a value is retrieved. Defaults to 1 +- initial-value - What should the initial value of the sequence + be. + + **NOTE** + + If you want to implement a cross-vendor compatible application you + have to specify and additionally define the + element, if Doctrine chooses the sequence strategy for a + platform. + + +Defining a Mapped Superclass +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you want to define a class that multiple entities inherit +from, which itself is not an entity however. The chapter on +*Inheritance Mapping* describes a Mapped Superclass in detail. You +can define it in XML using the ```` tag. + +.. code-block:: xml + + + + + + + + +Required attributes: + + +- name - Class name of the mapped superclass. + +You can nest any number of ```` and unidirectional +```` or ```` associations inside a +mapped superclass. + +Defining Inheritance Mappings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are currently two inheritance persistence strategies that you +can choose from when defining entities that inherit from each +other. Single Table inheritance saves the fields of the complete +inheritance hierarchy in a single table, joined table inheritance +creates a table for each entity combining the fields using join +conditions. + +You can specify the inheritance type in the ```` element +and then use the ```` and +```` attributes. + +.. code-block:: xml + + + + + + + + + + +The allowed values for inheritance-type attribute are ``JOINED`` or +``SINGLE_TABLE``. + +.. note:: + + All inheritance related definitions have to be defined on the root + entity of the hierarchy. + + +Defining Lifecycle Callbacks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define the lifecycle callback methods on your entities +using the ```` element: + +.. code-block:: xml + + + + + + + + +Defining One-To-One Relations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define One-To-One Relations/Associations using the +```` element. The required and optional attributes +depend on the associations being on the inverse or owning side. + +For the inverse side the mapping is as simple as: + +.. code-block:: xml + + + + + +Required attributes for inverse One-To-One: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! +- mapped-by - Name of the field on the owning side (here Address + entity) that contains the owning side association. + +For the owning side this mapping would look like: + +.. code-block:: xml + + + + + +Required attributes for owning One-to-One: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes for owning One-to-One: + + +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- orphan-removal - If true, the inverse side entity is always + deleted when the owning side entity is. Defaults to false. +- fetch - Either LAZY or EAGER, defaults to LAZY. This attribute + makes only sense on the owning side, the inverse side *ALWAYS* has + to use the ``FETCH`` strategy. + +The definition for the owning side relies on a bunch of mapping +defaults for the join column names. Without the nested +```` element Doctrine assumes to foreign key to be +called ``user_id`` on the Address Entities table. This is because +the ``MyProject\Address`` entity is the owning side of this +association, which means it contains the foreign key. + +The completed explicitly defined mapping is: + +.. code-block:: xml + + + + + + + +Defining Many-To-One Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The many-to-one association is *ALWAYS* the owning side of any +bidirectional association. This simplifies the mapping compared to +the one-to-one case. The minimal mapping for this association looks +like: + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- orphan-removal - If true the entity on the inverse side is + always deleted when the owning side entity is and it is not + connected to any other owning side entity anymore. Defaults to + false. +- fetch - Either LAZY or EAGER, defaults to LAZY. + +This definition relies on a bunch of mapping defaults with regards +to the naming of the join-column/foreign key. The explicitly +defined mapping includes a ```` tag nested inside +the many-to-one association tag: + +.. code-block:: xml + + + + + + + +The join-column attribute ``name`` specifies the column name of the +foreign key and the ``referenced-column-name`` attribute specifies +the name of the primary key column on the User entity. + +Defining One-To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The one-to-many association is *ALWAYS* the inverse side of any +association. There exists no such thing as a uni-directional +one-to-many association, which means this association only ever +exists for bi-directional associations. + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! +- mapped-by - Name of the field on the owning side (here + Phonenumber entity) that contains the owning side association. + +Optional attributes: + + +- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY. +- index-by: Index the collection by a field on the target entity. + +Defining Many-To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +From all the associations the many-to-many has the most complex +definition. When you rely on the mapping defaults you can omit many +definitions and rely on their implicit values. + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- mapped-by - Name of the field on the owning side that contains + the owning side association if the defined many-to-many association + is on the inverse side. +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY. +- index-by: Index the collection by a field on the target entity. + +The mapping defaults would lead to a join-table with the name +"User\_Group" being created that contains two columns "user\_id" +and "group\_id". The explicit definition of this mapping would be: + +.. code-block:: xml + + + + + + + + + + + + + + +Here both the ```` and ```` +tags are necessary to tell Doctrine for which side the specified +join-columns apply. These are nested inside a ```` +attribute which allows to specify the table name of the +many-to-many join-table. + +Cascade Element +~~~~~~~~~~~~~~~ + +Doctrine allows cascading of several UnitOfWork operations to +related entities. You can specify the cascade operations in the +```` element inside any of the association mapping +tags. + +.. code-block:: xml + + + + + + + + + +Besides ```` the following operations can be +specified by their respective tags: + + +- ```` +- ```` +- ```` +- ```` + +Join Column Element +~~~~~~~~~~~~~~~~~~~ + +In any explicitly defined association mapping you will need the +```` tag. It defines how the foreign key and primary +key names are called that are used for joining two entities. + +Required attributes: + + +- name - The column name of the foreign key. +- referenced-column-name - The column name of the associated + entities primary key + +Optional attributes: + + +- unique - If the join column should contain a UNIQUE constraint. + This makes sense for Many-To-Many join-columns only to simulate a + one-to-many unidirectional using a join-table. +- nullable - should the join column be nullable, defaults to true. +- on-delete - Foreign Key Cascade action to perform when entity is + deleted, defaults to NO ACTION/RESTRICT but can be set to + "CASCADE". + +Defining Order of To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can require one-to-many or many-to-many associations to be +retrieved using an additional ``ORDER BY``. + +.. code-block:: xml + + + + + + + + + +Defining Indexes or Unique Constraints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To define additional indexes or unique constraints on the entities +table you can use the ```` and +```` elements: + +.. code-block:: xml + + + + + + + + + + + + + +You have to specify the column and not the entity-class field names +in the index and unique-constraint definitions. + +Derived Entities ID syntax +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the primary key of an entity contains a foreign key to another entity we speak of a derived +entity relationship. You can define this in XML with the "association-key" attribute in the ```` tag. + +.. code-block:: xml + + + + + + + + + + + + + diff --git a/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst b/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst new file mode 100644 index 00000000000..ea54e277ae9 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst @@ -0,0 +1,154 @@ +YAML Mapping +============ + +The YAML mapping driver enables you to provide the ORM metadata in +form of YAML documents. + +The YAML mapping document of a class is loaded on-demand the first +time it is requested and subsequently stored in the metadata cache. +In order to work, this requires certain conventions: + + +- Each entity/mapped superclass must get its own dedicated YAML + mapping document. +- The name of the mapping document must consist of the fully + qualified name of the class, where namespace separators are + replaced by dots (.). +- All mapping documents should get the extension ".dcm.yml" to + identify it as a Doctrine mapping file. This is more of a + convention and you are not forced to do this. You can change the + file extension easily enough. + +.. code-block:: php + + setFileExtension('.yml'); + +It is recommended to put all YAML mapping documents in a single +folder but you can spread the documents over several folders if you +want to. In order to tell the YamlDriver where to look for your +mapping documents, supply an array of paths as the first argument +of the constructor, like this: + +.. code-block:: php + + setMetadataDriverImpl($driver); + +Simplified YAML Driver +~~~~~~~~~~~~~~~~~~~~~~ + +The Symfony project sponsored a driver that simplifies usage of the YAML Driver. +The changes between the original driver are: + +- File Extension is .orm.yml +- Filenames are shortened, "MyProject\\Entities\\User" will become User.orm.yml +- You can add a global file and add multiple entities in this file. + +Configuration of this client works a little bit different: + +.. code-block:: php + + 'MyProject\Entities', + '/path/to/files2' => 'OtherProject\Entities' + ); + $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver($namespaces); + $driver->setGlobalBasename('global'); // global.orm.yml + +Example +------- + +As a quick start, here is a small example document that makes use +of several common elements: + +.. code-block:: yaml + + # Doctrine.Tests.ORM.Mapping.User.dcm.yml + Doctrine\Tests\ORM\Mapping\User: + type: entity + repositoryClass: Doctrine\Tests\ORM\Mapping\UserRepository + table: cms_users + schema: schema_name # The schema the table lies in, for platforms that support schemas (Optional, >= 2.5) + readOnly: true + indexes: + name_index: + columns: [ name ] + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + length: 50 + email: + type: string + length: 32 + column: user_email + unique: true + options: + fixed: true + comment: User's email address + loginCount: + type: integer + column: login_count + nullable: false + options: + unsigned: true + default: 0 + oneToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + onDelete: CASCADE + oneToMany: + phonenumbers: + targetEntity: Phonenumber + mappedBy: user + cascade: ["persist", "merge"] + manyToMany: + groups: + targetEntity: Group + joinTable: + name: cms_users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + postPersist: [ doStuffOnPostPersist ] + +Be aware that class-names specified in the YAML files should be +fully qualified. + +Reference +~~~~~~~~~~~~~~~~~~~~~~ + +Unique Constraints +------------------ + +It is possible to define unique constraints by the following declaration: + +.. code-block:: yaml + + # ECommerceProduct.orm.yml + ECommerceProduct: + type: entity + fields: + # definition of some fields + uniqueConstraints: + search_idx: + columns: [ name, email ] + diff --git a/vendor/doctrine/orm/docs/en/toc.rst b/vendor/doctrine/orm/docs/en/toc.rst new file mode 100644 index 00000000000..0df70414f68 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/toc.rst @@ -0,0 +1,87 @@ +Welcome to Doctrine 2 ORM's documentation! +========================================== + +Tutorials +--------- + +.. toctree:: + :maxdepth: 1 + + tutorials/getting-started + tutorials/getting-started-database + tutorials/getting-started-models + tutorials/working-with-indexed-associations + tutorials/extra-lazy-associations + tutorials/composite-primary-keys + tutorials/ordered-associations + tutorials/override-field-association-mappings-in-subclasses + tutorials/pagination.rst + tutorials/embeddables.rst + +Reference Guide +--------------- + +.. toctree:: + :maxdepth: 1 + :numbered: + + reference/architecture + reference/configuration.rst + reference/faq + reference/basic-mapping + reference/association-mapping + reference/inheritance-mapping + reference/working-with-objects + reference/working-with-associations + reference/events + reference/unitofwork + reference/unitofwork-associations + reference/transactions-and-concurrency + reference/batch-processing + reference/dql-doctrine-query-language + reference/query-builder + reference/native-sql + reference/change-tracking-policies + reference/partial-objects + reference/xml-mapping + reference/yaml-mapping + reference/annotations-reference + reference/php-mapping + reference/caching + reference/improving-performance + reference/tools + reference/metadata-drivers + reference/best-practices + reference/limitations-and-known-issues + tutorials/pagination + reference/filters + reference/namingstrategy + reference/advanced-configuration + reference/second-level-cache + reference/security + + +Cookbook +-------- + +.. toctree:: + :maxdepth: 1 + + cookbook/aggregate-fields + cookbook/custom-mapping-types + cookbook/decorator-pattern + cookbook/dql-custom-walkers + cookbook/dql-user-defined-functions + cookbook/implementing-arrayaccess-for-domain-objects + cookbook/implementing-the-notify-changetracking-policy + cookbook/implementing-wakeup-or-clone + cookbook/integrating-with-codeigniter + cookbook/resolve-target-entity-listener + cookbook/sql-table-prefixes + cookbook/strategy-cookbook-introduction + cookbook/validation-of-entities + cookbook/working-with-datetime + cookbook/mysql-enums + cookbook/advanced-field-value-conversion-using-custom-mapping-types + cookbook/entities-in-session + diff --git a/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst b/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst new file mode 100644 index 00000000000..dd4e49e04aa --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst @@ -0,0 +1,375 @@ +Composite and Foreign Keys as Primary Key +========================================= + +.. versionadded:: 2.1 + +Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept +and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases. +For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as +primary keys are supported. + +This tutorial shows how the semantics of composite primary keys work and how they map to the database. + +General Considerations +~~~~~~~~~~~~~~~~~~~~~~ + +Every entity with a composite key cannot use an id generator other than "ASSIGNED". That means +the ID fields have to have their values set before you call ``EntityManager#persist($entity)``. + +Primitive Types only +~~~~~~~~~~~~~~~~~~~~ + +Even in version 2.0 you can have composite keys as long as they only consist of the primitive types +``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name +and year of production as primary keys: + +.. configuration-block:: + + .. code-block:: php + + name = $name; + $this->year = $year; + } + + public function getModelName() + { + return $this->name; + } + + public function getYearOfProduction() + { + return $this->year; + } + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: yaml + + VehicleCatalogue\Model\Car: + type: entity + id: + name: + type: string + year: + type: integer + +Now you can use this entity: + +.. code-block:: php + + persist($car); + $em->flush(); + +And for querying you can use arrays to both DQL and EntityRepositories: + +.. code-block:: php + + find("VehicleCatalogue\Model\Car", array("name" => "Audi A8", "year" => 2010)); + + $dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.id = ?1"; + $audi = $em->createQuery($dql) + ->setParameter(1, array("name" => "Audi A8", "year" => 2010)) + ->getSingleResult(); + +You can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name`` +and to ``year`` to the related entities. + +.. note:: + + This example shows how you can nicely solve the requirement for existing + values before ``EntityManager#persist()``: By adding them as mandatory values for the constructor. + +Identity through foreign Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + Identity through foreign entities is only supported with Doctrine 2.1 + +There are tons of use-cases where the identity of an Entity should be determined by the entity +of one or many parent entities. + +- Dynamic Attributes of an Entity (for example Article). Each Article has many + attributes with primary key "article_id" and "attribute_name". +- Address object of a Person, the primary key of the address is "user_id". This is not a case of a composite primary + key, but the identity is derived through a foreign entity and a foreign key. +- Join Tables with metadata can be modelled as Entity, for example connections between two articles + with a little description and a score. + +The semantics of mapping identity through foreign entities are easy: + +- Only allowed on Many-To-One or One-To-One associations. +- Plug an ``@Id`` annotation onto every association. +- Set an attribute ``association-key`` with the field name of the association in XML. +- Set a key ``associationKey:`` with the field name of the association in YAML. + +Use-Case 1: Dynamic Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We keep up the example of an Article with arbitrary attributes, the mapping looks like this: + +.. configuration-block:: + + .. code-block:: php + + attributes[$name] = new ArticleAttribute($name, $value, $this); + } + } + + /** + * @Entity + */ + class ArticleAttribute + { + /** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */ + private $article; + + /** @Id @Column(type="string") */ + private $attribute; + + /** @Column(type="string") */ + private $value; + + public function __construct($name, $value, $article) + { + $this->attribute = $name; + $this->value = $value; + $this->article = $article; + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: yaml + + Application\Model\ArticleAttribute: + type: entity + id: + article: + associationKey: true + attribute: + type: string + fields: + value: + type: string + manyToOne: + article: + targetEntity: Article + inversedBy: attributes + + +Use-Case 2: Simple Derived Identity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you have the requirement that two objects are related by a One-To-One association +and that the dependent class should re-use the primary key of the class it depends on. +One good example for this is a user-address relationship: + +.. configuration-block:: + + .. code-block:: php + + customer = $customer; + $this->items = new ArrayCollection(); + $this->created = new \DateTime("now"); + } + } + + /** @Entity */ + class Product + { + /** @Id @Column(type="integer") @GeneratedValue */ + private $id; + + /** @Column(type="string") */ + private $name; + + /** @Column(type="decimal") */ + private $currentPrice; + + public function getCurrentPrice() + { + return $this->currentPrice; + } + } + + /** @Entity */ + class OrderItem + { + /** @Id @ManyToOne(targetEntity="Order") */ + private $order; + + /** @Id @ManyToOne(targetEntity="Product") */ + private $product; + + /** @Column(type="integer") */ + private $amount = 1; + + /** @Column(type="decimal") */ + private $offeredPrice; + + public function __construct(Order $order, Product $product, $amount = 1) + { + $this->order = $order; + $this->product = $product; + $this->offeredPrice = $product->getCurrentPrice(); + } + } + + +Performance Considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using composite keys always comes with a performance hit compared to using entities with +a simple surrogate key. This performance impact is mostly due to additional PHP code that is +necessary to handle this kind of keys, most notably when using derived identifiers. + +On the SQL side there is not much overhead as no additional or unexpected queries have to be +executed to manage entities with derived foreign keys. diff --git a/vendor/doctrine/orm/docs/en/tutorials/embeddables.rst b/vendor/doctrine/orm/docs/en/tutorials/embeddables.rst new file mode 100644 index 00000000000..60265d6d5ae --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/embeddables.rst @@ -0,0 +1,162 @@ +Separating Concerns using Embeddables +------------------------------------- + +Embeddables are classes which are not entities themself, but are embedded +in entities and can also be queried in DQL. You'll mostly want to use them +to reduce duplication or separating concerns. Value objects such as date range +or address are the primary use case for this feature. Embeddables can only +contain properties with basic ``@Column`` mapping. + +For the purposes of this tutorial, we will assume that you have a ``User`` +class in your application and you would like to store an address in +the ``User`` class. We will model the ``Address`` class as an embeddable +instead of simply adding the respective columns to the ``User`` class. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + embedded: + address: + class: Address + + Address: + type: embeddable + fields: + street: { type: string } + postalCode: { type: string } + city: { type: string } + country: { type: string } + +In terms of your database schema, Doctrine will automatically inline all +columns from the ``Address`` class into the table of the ``User`` class, +just as if you had declared them directly there. + +Column Prefixing +---------------- + +By default, Doctrine names your columns by prefixing them, using the value +object name. + +Following the example above, your columns would be named as ``address_street``, +``address_postalCode``... + +You can change this behaviour to meet your needs by changing the +``columnPrefix`` attribute in the ``@Embedded`` notation. + +The following example shows you how to set your prefix to ``myPrefix_``: + +.. configuration-block:: + + .. code-block:: php + + + + + + .. code-block:: yaml + + User: + type: entity + embedded: + address: + class: Address + columnPrefix: myPrefix_ + +To have Doctrine drop the prefix and use the value object's property name +directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML): + +.. configuration-block:: + + .. code-block:: php + + + + + + +DQL +--- + +You can also use mapped fields of embedded classes in DQL queries, just +as if they were declared in the ``User`` class: + +.. code-block:: sql + + SELECT u FROM User u WHERE u.address.city = :myCity + diff --git a/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst new file mode 100644 index 00000000000..456c8c1c5ff --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst @@ -0,0 +1,86 @@ +Extra Lazy Associations +======================= + +.. versionadded:: 2.1 + +In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. +where posts can be commented, you always have to assume that a post draws hundreds of comments. +In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This +can lead to pretty serious performance problems, if your associations contain several hundreds or thousands +of entities. + +With Doctrine 2.1 a feature called **Extra Lazy** is introduced for associations. Associations +are marked as **Lazy** by default, which means the whole collection object for an association is populated +the first time its accessed. If you mark an association as extra lazy the following methods on collections +can be called without triggering a full load of the collection: + +- ``Collection#contains($entity)`` +- ``Collection#containsKey($key)`` (available with Doctrine 2.5) +- ``Collection#count()`` +- ``Collection#get($key)`` (available with Doctrine 2.4) +- ``Collection#slice($offset, $length = null)`` + +For each of the above methods the following semantics apply: + +- For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database. +- For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed. + +Additionally even with Doctrine 2.0 the following methods do not trigger the collection load: + +- ``Collection#add($entity)`` +- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does + not work when setting specific keys like ``$coll[0] = $entity``. + +With extra lazy collections you can now not only add entities to large collections but also paginate them +easily using a combination of ``count`` and ``slice``. + + +Enabling Extra-Lazy Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The mapping configuration is simple. Instead of using the default value of ``fetch="LAZY"`` you have to +switch to extra lazy as shown in these examples: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\CMS\CmsGroup: + type: entity + # ... + manyToMany: + users: + targetEntity: CmsUser + mappedBy: groups + fetch: EXTRA_LAZY + diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst new file mode 100644 index 00000000000..c625193aeb5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst @@ -0,0 +1,27 @@ +Getting Started: Database First +=============================== + +.. note:: *Development Workflows* + + When you :doc:`Code First `, you + start with developing Objects and then map them onto your database. When + you :doc:`Model First `, you are modelling your application using tools (for + example UML) and generate database schema and PHP code from this model. + When you have a :doc:`Database First `, you already have a database schema + and generate the corresponding PHP code from it. + +.. note:: + + This getting started guide is in development. + +Development of new applications often starts with an existing database schema. +When the database schema is the starting point for your application, then +development is said to use the *Database First* approach to Doctrine. + +In this workflow you would modify the database schema first and then +regenerate the PHP code to use with this schema. You need a flexible +code-generator for this task and up to Doctrine 2.2, the code generator hasn't +been flexible enough to achieve this. + +We spinned off a subproject, Doctrine CodeGenerator, that will fill this gap and +allow you to do *Database First* development. diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst new file mode 100644 index 00000000000..e844b4d6865 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst @@ -0,0 +1,24 @@ +Getting Started: Model First +============================ + +.. note:: *Development Workflows* + + When you :doc:`Code First `, you + start with developing Objects and then map them onto your database. When + you :doc:`Model First `, you are modelling your application using tools (for + example UML) and generate database schema and PHP code from this model. + When you have a :doc:`Database First `, then you already have a database schema + and generate the corresponding PHP code from it. + +.. note:: + + This getting started guide is in development. + +There are applications when you start with a high-level description of the +model using modelling tools such as UML. Modelling tools could also be Excel, +XML or CSV files that describe the model in some structured way. If your +application is using a modelling tool, then the development workflow is said to +be a *Model First* approach to Doctrine2. + +In this workflow you always change the model description and then regenerate +both PHP code and database schema from this model. diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst new file mode 100644 index 00000000000..1abcfb548a3 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst @@ -0,0 +1,1549 @@ +Getting Started with Doctrine +============================= + +This guide covers getting started with the Doctrine ORM. After working +through the guide you should know: + +- How to install and configure Doctrine by connecting it to a database +- Mapping PHP objects to database tables +- Generating a database schema from PHP objects +- Using the ``EntityManager`` to insert, update, delete and find + objects in the database. + +Guide Assumptions +----------------- + +This guide is designed for beginners that haven't worked with Doctrine ORM +before. There are some prerequesites for the tutorial that have to be +installed: + +- PHP 5.4 or above +- Composer Package Manager (`Install Composer + `_) + +The code of this tutorial is `available on Github `_. + +.. note:: + + This tutorial assumes you work with **Doctrine 2.4** and above. + Some of the code will not work with lower versions. + +What is Doctrine? +----------------- + +Doctrine 2 is an `object-relational mapper (ORM) +`_ for PHP 5.4+ that +provides transparent persistence for PHP objects. It uses the Data Mapper +pattern at the heart, aiming for a complete separation of your domain/business +logic from the persistence in a relational database management system. + +The benefit of Doctrine for the programmer is the ability to focus +on the object-oriented business logic and worry about persistence only +as a secondary problem. This doesn't mean persistence is downplayed by Doctrine +2, however it is our belief that there are considerable benefits for +object-oriented programming if persistence and entities are kept +separated. + +What are Entities? +~~~~~~~~~~~~~~~~~~ + +Entities are PHP Objects that can be identified over many requests +by a unique identifier or primary key. These classes don't need to extend any +abstract base class or interface. An entity class must not be final +or contain final methods. Additionally it must not implement +**clone** nor **wakeup** or :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`. + +An entity contains persistable properties. A persistable property +is an instance variable of the entity that is saved into and retrieved from the database +by Doctrine's data mapping capabilities. + +An Example Model: Bug Tracker +----------------------------- + +For this Getting Started Guide for Doctrine we will implement the +Bug Tracker domain model from the +`Zend\_Db\_Table `_ +documentation. Reading their documentation we can extract the +requirements: + +- A Bug has a description, creation date, status, reporter and + engineer +- A Bug can occur on different Products (platforms) +- A Product has a name. +- Bug reporters and engineers are both Users of the system. +- A User can create new Bugs. +- The assigned engineer can close a Bug. +- A User can see all his reported or assigned Bugs. +- Bugs can be paginated through a list-view. + +Project Setup +------------- + +Create a new empty folder for this tutorial project, for example +``doctrine2-tutorial`` and create a new file ``composer.json`` with +the following contents: + +:: + + { + "require": { + "doctrine/orm": "2.4.*", + "symfony/yaml": "2.*" + }, + "autoload": { + "psr-0": {"": "src/"} + } + } + + +Install Doctrine using the Composer Dependency Management tool, by calling: + +:: + + $ composer install + +This will install the packages Doctrine Common, Doctrine DBAL, Doctrine ORM, +Symfony YAML and Symfony Console into the `vendor` directory. The Symfony +dependencies are not required by Doctrine but will be used in this tutorial. + +Add the following directories: +:: + + doctrine2-tutorial + |-- config + | |-- xml + | `-- yaml + `-- src + +Obtaining the EntityManager +--------------------------- + +Doctrine's public interface is the EntityManager, it provides the +access point to the complete lifecycle management of your entities +and transforms entities from and back to persistence. You have to +configure and create it to use your entities with Doctrine 2. I +will show the configuration steps and then discuss them step by +step: + +.. code-block:: php + + 'pdo_sqlite', + 'path' => __DIR__ . '/db.sqlite', + ); + + // obtaining the entity manager + $entityManager = EntityManager::create($conn, $config); + +The first require statement sets up the autoloading capabilities of Doctrine +using the Composer autoload. + +The second block consists of the instantiation of the ORM +``Configuration`` object using the Setup helper. It assumes a bunch +of defaults that you don't have to bother about for now. You can +read up on the configuration details in the +:doc:`reference chapter on configuration <../reference/configuration>`. + +The third block shows the configuration options required to connect +to a database, in my case a file-based sqlite database. All the +configuration options for all the shipped drivers are given in the +`DBAL Configuration section of the manual `_. + +The last block shows how the ``EntityManager`` is obtained from a +factory method. + +Generating the Database Schema +------------------------------ + +Now that we have defined the Metadata mappings and bootstrapped the +EntityManager we want to generate the relational database schema +from it. Doctrine has a Command-Line Interface that allows you to +access the SchemaTool, a component that generates the required +tables to work with the metadata. + +For the command-line tool to work a cli-config.php file has to be +present in the project root directory, where you will execute the +doctrine command. Its a fairly simple file: + +.. code-block:: php + + id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + } + +Note that all fields are set to protected (not public) with a +mutator (getter and setter) defined for every field except $id. +The use of mutators allows Doctrine to hook into calls which +manipulate the entities in ways that it could not if you just +directly set the values with ``entity#field = foo;`` + +The id field has no setter since, generally speaking, your code +should not set this value since it represents a database id value. +(Note that Doctrine itself can still set the value using the +Reflection API instead of a defined setter function) + +The next step for persistence with Doctrine is to describe the +structure of the ``Product`` entity to Doctrine using a metadata +language. The metadata language describes how entities, their +properties and references should be persisted and what constraints +should be applied to them. + +Metadata for entities are configured using a XML, YAML or Docblock Annotations. +This Getting Started Guide will show the mappings for all Mapping Drivers. +References in the text will be made to the XML mapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + .. code-block:: yaml + + # config/yaml/Product.dcm.yml + Product: + type: entity + table: products + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + +The top-level ``entity`` definition tag specifies information about +the class and table-name. The primitive type ``Product#name`` is +defined as a ``field`` attribute. The ``id`` property is defined with +the ``id`` tag, this has a ``generator`` tag nested inside which +defines that the primary key generation mechanism automatically +uses the database platforms native id generation strategy (for +example AUTO INCREMENT in the case of MySql or Sequences in the +case of PostgreSql and Oracle). + +Now that we have defined our first entity, let's update the database: + +:: + + $ vendor/bin/doctrine orm:schema-tool:update --force --dump-sql + +Specifying both flags ``--force`` and ``-dump-sql`` prints and executes the DDL +statements. + +Now create a new script that will insert products into the database: + +.. code-block:: php + + setName($newProductName); + + $entityManager->persist($product); + $entityManager->flush(); + + echo "Created Product with ID " . $product->getId() . "\n"; + +Call this script from the command-line to see how new products are created: + +:: + + $ php create_product.php ORM + $ php create_product.php DBAL + +What is happening here? Using the ``Product`` is pretty standard OOP. +The interesting bits are the use of the ``EntityManager`` service. To +notify the EntityManager that a new entity should be inserted into the database +you have to call ``persist()``. To initiate a transaction to actually perform +the insertion, You have to explicitly call ``flush()`` on the ``EntityManager``. + +This distinction between persist and flush is allows to aggregate all writes +(INSERT, UPDATE, DELETE) into one single transaction, which is executed when +flush is called. Using this approach the write-performance is significantly +better than in a scenario where updates are done for each entity in isolation. + +Doctrine follows the UnitOfWork pattern which additionally detects all entities +that were fetched and have changed during the request. You don't have to keep track of +entities yourself, when Doctrine already knows about them. + +As a next step we want to fetch a list of all the Products. Let's create a +new script for this: + +.. code-block:: php + + getRepository('Product'); + $products = $productRepository->findAll(); + + foreach ($products as $product) { + echo sprintf("-%s\n", $product->getName()); + } + +The ``EntityManager#getRepository()`` method can create a finder object (called +a repository) for every entity. It is provided by Doctrine and contains some +finder methods such as ``findAll()``. + +Let's continue with displaying the name of a product based on its ID: + +.. code-block:: php + + + require_once "bootstrap.php"; + + $id = $argv[1]; + $product = $entityManager->find('Product', $id); + + if ($product === null) { + echo "No product found.\n"; + exit(1); + } + + echo sprintf("-%s\n", $product->getName()); + +Updating a product name demonstrates the functionality UnitOfWork of pattern +discussed before. We only need to find a product entity and all changes to its +properties are written to the database: + +.. code-block:: php + + + require_once "bootstrap.php"; + + $id = $argv[1]; + $newName = $argv[2]; + + $product = $entityManager->find('Product', $id); + + if ($product === null) { + echo "Product $id does not exist.\n"; + exit(1); + } + + $product->setName($newName); + + $entityManager->flush(); + +After calling this script on one of the existing products, you can verify the +product name changed by calling the ``show_product.php`` script. + +Adding Bug and User Entities +---------------------------- + +We continue with the bug tracker domain, by creating the missing classes +``Bug`` and ``User`` and putting them into ``src/Bug.php`` and +``src/User.php`` respectively. + +.. code-block:: php + + id; + } + + public function getDescription() + { + return $this->description; + } + + public function setDescription($description) + { + $this->description = $description; + } + + public function setCreated(DateTime $created) + { + $this->created = $created; + } + + public function getCreated() + { + return $this->created; + } + + public function setStatus($status) + { + $this->status = $status; + } + + public function getStatus() + { + return $this->status; + } + } + +.. code-block:: php + + id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + } + +All of the properties discussed so far are simple string and integer values, +for example the id fields of the entities, their names, description, status and +change dates. Next we will model the dynamic relationships between the entities +by defining the references between entities. + +References between objects are foreign keys in the database. You never have to +(and never should) work with the foreign keys directly, only with the objects +that represent the foreign key through their own identity. + +For every foreign key you either have a Doctrine ManyToOne or OneToOne +association. On the inverse sides of these foreign keys you can have +OneToMany associations. Obviously you can have ManyToMany associations +that connect two tables with each other through a join table with +two foreign keys. + +Now that you know the basics about references in Doctrine, we can extend the +domain model to match the requirements: + +.. code-block:: php + + products = new ArrayCollection(); + } + } + +.. code-block:: php + + reportedBugs = new ArrayCollection(); + $this->assignedBugs = new ArrayCollection(); + } + } + +Whenever an entity is recreated from the database, an Collection +implementation of the type Doctrine is injected into your entity +instead of an array. Compared to the ArrayCollection this +implementation helps the Doctrine ORM understand the changes that +have happened to the collection which are noteworthy for +persistence. + +.. warning:: + + Lazy load proxies always contain an instance of + Doctrine's EntityManager and all its dependencies. Therefore a + var\_dump() will possibly dump a very large recursive structure + which is impossible to render and read. You have to use + ``Doctrine\Common\Util\Debug::dump()`` to restrict the dumping to a + human readable level. Additionally you should be aware that dumping + the EntityManager to a Browser may take several minutes, and the + Debug::dump() method just ignores any occurrences of it in Proxy + instances. + +Because we only work with collections for the references we must be +careful to implement a bidirectional reference in the domain model. +The concept of owning or inverse side of a relation is central to +this notion and should always be kept in mind. The following +assumptions are made about relations and have to be followed to be +able to work with Doctrine 2. These assumptions are not unique to +Doctrine 2 but are best practices in handling database relations +and Object-Relational Mapping. + + +- Changes to Collections are saved or updated, when the entity on + the *owning* side of the collection is saved or updated. +- Saving an Entity at the inverse side of a relation never + triggers a persist operation to changes to the collection. +- In a one-to-one relation the entity holding the foreign key of + the related entity on its own database table is *always* the owning + side of the relation. +- In a many-to-many relation, both sides can be the owning side of + the relation. However in a bi-directional many-to-many relation + only one is allowed to be. +- In a many-to-one relation the Many-side is the owning side by + default, because it holds the foreign key. +- The OneToMany side of a relation is inverse by default, since + the foreign key is saved on the Many side. A OneToMany relation can + only be the owning side, if its implemented using a ManyToMany + relation with join table and restricting the one side to allow only + UNIQUE values per database constraint. + +.. note:: + + Consistency of bi-directional references on the inverse side of a + relation have to be managed in userland application code. Doctrine + cannot magically update your collections to be consistent. + + +In the case of Users and Bugs we have references back and forth to +the assigned and reported bugs from a user, making this relation +bi-directional. We have to change the code to ensure consistency of +the bi-directional reference: + +.. code-block:: php + + assignedToBug($this); + $this->engineer = $engineer; + } + + public function setReporter($reporter) + { + $reporter->addReportedBug($this); + $this->reporter = $reporter; + } + + public function getEngineer() + { + return $this->engineer; + } + + public function getReporter() + { + return $this->reporter; + } + } + +.. code-block:: php + + reportedBugs[] = $bug; + } + + public function assignedToBug($bug) + { + $this->assignedBugs[] = $bug; + } + } + +I chose to name the inverse methods in past-tense, which should +indicate that the actual assigning has already taken place and the +methods are only used for ensuring consistency of the references. +This approach is my personal preference, you can choose whatever +method to make this work. + +You can see from ``User#addReportedBug()`` and +``User#assignedToBug()`` that using this method in userland alone +would not add the Bug to the collection of the owning side in +``Bug#reporter`` or ``Bug#engineer``. Using these methods and +calling Doctrine for persistence would not update the collections +representation in the database. + +Only using ``Bug#setEngineer()`` or ``Bug#setReporter()`` +correctly saves the relation information. + +The ``Bug#reporter`` and ``Bug#engineer`` properties are +Many-To-One relations, which point to a User. In a normalized +relational model the foreign key is saved on the Bug's table, hence +in our object-relation model the Bug is at the owning side of the +relation. You should always make sure that the use-cases of your +domain model should drive which side is an inverse or owning one in +your Doctrine mapping. In our example, whenever a new bug is saved +or an engineer is assigned to the bug, we don't want to update the +User to persist the reference, but the Bug. This is the case with +the Bug being at the owning side of the relation. + +Bugs reference Products by an uni-directional ManyToMany relation in +the database that points from Bugs to Products. + +.. code-block:: php + + products[] = $product; + } + + public function getProducts() + { + return $this->products; + } + } + +We are now finished with the domain model given the requirements. +Lets add metadata mappings for the ``User`` and ``Bug`` as we did for +the ``Product`` before: + +.. configuration-block:: + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # config/yaml/Bug.dcm.yml + Bug: + type: entity + table: bugs + id: + id: + type: integer + generator: + strategy: AUTO + fields: + description: + type: text + created: + type: datetime + status: + type: string + manyToOne: + reporter: + targetEntity: User + inversedBy: reportedBugs + engineer: + targetEntity: User + inversedBy: assignedBugs + manyToMany: + products: + targetEntity: Product + + +Here we have the entity, id and primitive type definitions. +For the "created" field we have used the ``datetime`` type, +which translates the YYYY-mm-dd HH:mm:ss database format +into a PHP DateTime instance and back. + +After the field definitions the two qualified references to the +user entity are defined. They are created by the ``many-to-one`` +tag. The class name of the related entity has to be specified with +the ``target-entity`` attribute, which is enough information for +the database mapper to access the foreign-table. Since +``reporter`` and ``engineer`` are on the owning side of a +bi-directional relation we also have to specify the ``inversed-by`` +attribute. They have to point to the field names on the inverse +side of the relationship. We will see in the next example that the ``inversed-by`` +attribute has a counterpart ``mapped-by`` which makes that +the inverse side. + +The last definition is for the ``Bug#products`` collection. It +holds all products where the specific bug occurs. Again +you have to define the ``target-entity`` and ``field`` attributes +on the ``many-to-many`` tag. + +The last missing definition is that of the User entity: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # config/xml/User.dcm.yml + User: + type: entity + table: users + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + oneToMany: + reportedBugs: + targetEntity: Bug + mappedBy: reporter + assignedBugs: + targetEntity: Bug + mappedBy: engineer + +Here are some new things to mention about the ``one-to-many`` tags. +Remember that we discussed about the inverse and owning side. Now +both reportedBugs and assignedBugs are inverse relations, which +means the join details have already been defined on the owning +side. Therefore we only have to specify the property on the Bug +class that holds the owning sides. + +This example has a fair overview of the most basic features of the +metadata definition language. + +Update your database running: +:: + + $ vendor/bin/doctrine orm:schema-tool:update --force + + +Implementing more Requirements +------------------------------ + +For starters we need a create user entities: + +.. code-block:: php + + setName($newUsername); + + $entityManager->persist($user); + $entityManager->flush(); + + echo "Created User with ID " . $user->getId() . "\n"; + +Now call: + +:: + + $ php create_user.php beberlei + +We now have the data to create a bug and the code for this scenario may look +like this: + +.. code-block:: php + + find("User", $theReporterId); + $engineer = $entityManager->find("User", $theDefaultEngineerId); + if (!$reporter || !$engineer) { + echo "No reporter and/or engineer found for the input.\n"; + exit(1); + } + + $bug = new Bug(); + $bug->setDescription("Something does not work!"); + $bug->setCreated(new DateTime("now")); + $bug->setStatus("OPEN"); + + foreach ($productIds as $productId) { + $product = $entityManager->find("Product", $productId); + $bug->assignToProduct($product); + } + + $bug->setReporter($reporter); + $bug->setEngineer($engineer); + + $entityManager->persist($bug); + $entityManager->flush(); + + echo "Your new Bug Id: ".$bug->getId()."\n"; + +Since we only have one user and product, probably with the ID of 1, we can call this script with: + +:: + + php create_bug.php 1 1 1 + +This is the first contact with the read API of the EntityManager, +showing that a call to ``EntityManager#find($name, $id)`` returns a +single instance of an entity queried by primary key. Besides this +we see the persist + flush pattern again to save the Bug into the +database. + +See how simple relating Bug, Reporter, Engineer and Products is +done by using the discussed methods in the "A first prototype" +section. The UnitOfWork will detect this relations when flush is +called and relate them in the database appropriately. + +Queries for Application Use-Cases +--------------------------------- + +List of Bugs +~~~~~~~~~~~~ + +Using the previous examples we can fill up the database quite a +bit, however we now need to discuss how to query the underlying +mapper for the required view representations. When opening the +application, bugs can be paginated through a list-view, which is +the first read-only use-case: + +.. code-block:: php + + createQuery($dql); + $query->setMaxResults(30); + $bugs = $query->getResult(); + + foreach ($bugs as $bug) { + echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n"; + echo " Reported by: ".$bug->getReporter()->getName()."\n"; + echo " Assigned to: ".$bug->getEngineer()->getName()."\n"; + foreach ($bug->getProducts() as $product) { + echo " Platform: ".$product->getName()."\n"; + } + echo "\n"; + } + +The DQL Query in this example fetches the 30 most recent bugs with +their respective engineer and reporter in one single SQL statement. +The console output of this script is then: + +:: + + Something does not work! - 02.04.2010 + Reported by: beberlei + Assigned to: beberlei + Platform: My Product + +.. note:: + + **DQL is not SQL** + + You may wonder why we start writing SQL at the beginning of this + use-case. Don't we use an ORM to get rid of all the endless + hand-writing of SQL? Doctrine introduces DQL which is best + described as **object-query-language** and is a dialect of + `OQL `_ and + similar to `HQL `_ or + `JPQL `_. + It does not know the concept of columns and tables, but only those + of Entity-Class and property. Using the Metadata we defined before + it allows for very short distinctive and powerful queries. + + + An important reason why DQL is favourable to the Query API of most + ORMs is its similarity to SQL. The DQL language allows query + constructs that most ORMs don't, GROUP BY even with HAVING, + Sub-selects, Fetch-Joins of nested classes, mixed results with + entities and scalar data such as COUNT() results and much more. + Using DQL you should seldom come to the point where you want to + throw your ORM into the dumpster, because it doesn't support some + the more powerful SQL concepts. + + + Instead of handwriting DQL you can use the ``QueryBuilder`` retrieved + by calling ``$entityManager->createQueryBuilder()``. There are more + details about this in the relevant part of the documentation. + + + As a last resort you can still use Native SQL and a description of the + result set to retrieve entities from the database. DQL boils down to a + Native SQL statement and a ``ResultSetMapping`` instance itself. Using + Native SQL you could even use stored procedures for data retrieval, or + make use of advanced non-portable database queries like PostgreSql's + recursive queries. + + +Array Hydration of the Bug List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the previous use-case we retrieved the results as their +respective object instances. We are not limited to retrieving +objects only from Doctrine however. For a simple list view like the +previous one we only need read access to our entities and can +switch the hydration from objects to simple PHP arrays instead. + +Hydration can be an expensive process so only retrieving what you need can +yield considerable performance benefits for read-only requests. + +Implementing the same list view as before using array hydration we +can rewrite our code: + +.. code-block:: php + + createQuery($dql); + $bugs = $query->getArrayResult(); + + foreach ($bugs as $bug) { + echo $bug['description'] . " - " . $bug['created']->format('d.m.Y')."\n"; + echo " Reported by: ".$bug['reporter']['name']."\n"; + echo " Assigned to: ".$bug['engineer']['name']."\n"; + foreach ($bug['products'] as $product) { + echo " Platform: ".$product['name']."\n"; + } + echo "\n"; + } + +There is one significant difference in the DQL query however, we +have to add an additional fetch-join for the products connected to +a bug. The resulting SQL query for this single select statement is +pretty large, however still more efficient to retrieve compared to +hydrating objects. + +Find by Primary Key +~~~~~~~~~~~~~~~~~~~ + +The next Use-Case is displaying a Bug by primary key. This could be +done using DQL as in the previous example with a where clause, +however there is a convenience method on the ``EntityManager`` that +handles loading by primary key, which we have already seen in the +write scenarios: + +.. code-block:: php + + find("Bug", (int)$theBugId); + + echo "Bug: ".$bug->getDescription()."\n"; + echo "Engineer: ".$bug->getEngineer()->getName()."\n"; + +The output of the engineer’s name is fetched from the database! What is happening? + +Since we only retrieved the bug by primary key both the engineer and reporter +are not immediately loaded from the database but are replaced by LazyLoading +proxies. These proxies will load behind the scenes, when the first method +is called on them. + +Sample code of this proxy generated code can be found in the specified Proxy +Directory, it looks like: + +.. code-block:: php + + _load(); + return parent::addReportedBug($bug); + } + + public function assignedToBug($bug) + { + $this->_load(); + return parent::assignedToBug($bug); + } + } + +See how upon each method call the proxy is lazily loaded from the +database? + +The call prints: + +:: + + $ php show_bug.php 1 + Bug: Something does not work! + Engineer: beberlei + +.. warning:: + + Lazy loading additional data can be very convenient but the additional + queries create an overhead. If you know that certain fields will always + (or usually) be required by the query then you will get better performance + by explicitly retrieving them all in the first query. + + +Dashboard of the User +--------------------- + +For the next use-case we want to retrieve the dashboard view, a +list of all open bugs the user reported or was assigned to. This +will be achieved using DQL again, this time with some WHERE clauses +and usage of bound parameters: + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $theUserId) + ->setMaxResults(15) + ->getResult(); + + echo "You have created or assigned to " . count($myBugs) . " open bugs:\n\n"; + + foreach ($myBugs as $bug) { + echo $bug->getId() . " - " . $bug->getDescription()."\n"; + } + +Number of Bugs +-------------- + +Until now we only retrieved entities or their array representation. +Doctrine also supports the retrieval of non-entities through DQL. +These values are called "scalar result values" and may even be +aggregate values using COUNT, SUM, MIN, MAX or AVG functions. + +We will need this knowledge to retrieve the number of open bugs +grouped by product: + +.. code-block:: php + + createQuery($dql)->getScalarResult(); + + foreach ($productBugs as $productBug) { + echo $productBug['name']." has " . $productBug['openBugs'] . " open bugs!\n"; + } + +Updating Entities +----------------- + +There is a single use-case missing from the requirements, Engineers +should be able to close a bug. This looks like: + +.. code-block:: php + + status = "CLOSE"; + } + } + +.. code-block:: php + + find("Bug", (int)$theBugId); + $bug->close(); + + $entityManager->flush(); + +When retrieving the Bug from the database it is inserted into the +IdentityMap inside the UnitOfWork of Doctrine. This means your Bug +with exactly this id can only exist once during the whole request +no matter how often you call ``EntityManager#find()``. It even +detects entities that are hydrated using DQL and are already +present in the Identity Map. + +When flush is called the EntityManager loops over all the entities +in the identity map and performs a comparison between the values +originally retrieved from the database and those values the entity +currently has. If at least one of these properties is different the +entity is scheduled for an UPDATE against the database. Only the +changed columns are updated, which offers a pretty good performance +improvement compared to updating all the properties. + +Entity Repositories +------------------- + +For now we have not discussed how to separate the Doctrine query logic from your model. +In Doctrine 1 there was the concept of ``Doctrine_Table`` instances for this +separation. The similar concept in Doctrine2 is called Entity Repositories, integrating +the `repository pattern `_ at the heart of Doctrine. + +Every Entity uses a default repository by default and offers a bunch of convenience +methods that you can use to query for instances of that Entity. Take for example +our Product entity. If we wanted to Query by name, we can use: + +.. code-block:: php + + getRepository('Product') + ->findOneBy(array('name' => $productName)); + +The method ``findOneBy()`` takes an array of fields or association keys and the values to match against. + +If you want to find all entities matching a condition you can use ``findBy()``, for +example querying for all closed bugs: + +.. code-block:: php + + getRepository('Bug') + ->findBy(array('status' => 'CLOSED')); + + foreach ($bugs as $bug) { + // do stuff + } + +Compared to DQL these query methods are falling short of functionality very fast. +Doctrine offers you a convenient way to extend the functionalities of the default ``EntityRepository`` +and put all the specialized DQL query logic on it. For this you have to create a subclass +of ``Doctrine\ORM\EntityRepository``, in our case a ``BugRepository`` and group all +the previously discussed query functionality in it: + +.. code-block:: php + + getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getResult(); + } + + public function getRecentBugsArray($number = 30) + { + $dql = "SELECT b, e, r, p FROM Bug b JOIN b.engineer e ". + "JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC"; + $query = $this->getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getArrayResult(); + } + + public function getUsersBugs($userId, $number = 15) + { + $dql = "SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ". + "WHERE b.status = 'OPEN' AND e.id = ?1 OR r.id = ?1 ORDER BY b.created DESC"; + + return $this->getEntityManager()->createQuery($dql) + ->setParameter(1, $userId) + ->setMaxResults($number) + ->getResult(); + } + + public function getOpenBugsByProduct() + { + $dql = "SELECT p.id, p.name, count(b.id) AS openBugs FROM Bug b ". + "JOIN b.products p WHERE b.status = 'OPEN' GROUP BY p.id"; + return $this->getEntityManager()->createQuery($dql)->getScalarResult(); + } + } + +To be able to use this query logic through ``$this->getEntityManager()->getRepository('Bug')`` +we have to adjust the metadata slightly. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + .. code-block:: yaml + + Bug: + type: entity + repositoryClass: BugRepository + +Now we can remove our query logic in all the places and instead use them through the EntityRepository. +As an example here is the code of the first use case "List of Bugs": + +.. code-block:: php + + getRepository('Bug')->getRecentBugs(); + + foreach ($bugs as $bug) { + echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n"; + echo " Reported by: ".$bug->getReporter()->getName()."\n"; + echo " Assigned to: ".$bug->getEngineer()->getName()."\n"; + foreach ($bug->getProducts() as $product) { + echo " Platform: ".$product->getName()."\n"; + } + echo "\n"; + } + +Using EntityRepositories you can avoid coupling your model with specific query logic. +You can also re-use query logic easily throughout your application. + +Conclusion +---------- + +This tutorial is over here, I hope you had fun. Additional content +will be added to this tutorial incrementally, topics will include: + +- More on Association Mappings +- Lifecycle Events triggered in the UnitOfWork +- Ordering of Collections + +Additional details on all the topics discussed here can be found in +the respective manual chapters. diff --git a/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst new file mode 100644 index 00000000000..121bd145d57 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst @@ -0,0 +1,110 @@ +Ordering To-Many Associations +----------------------------- + +There are use-cases when you'll want to sort collections when they are +retrieved from the database. In userland you do this as long as you +haven't initially saved an entity with its associations into the +database. To retrieve a sorted collection from the database you can +use the ``@OrderBy`` annotation with an collection that specifies +an DQL snippet that is appended to all queries with this +collection. + +Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you +can specify the ``@OrderBy`` in the following way: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + orderBy: { 'name': 'ASC' } + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + +The DQL Snippet in OrderBy is only allowed to consist of +unqualified, unquoted field names and of an optional ASC/DESC +positional statement. Multiple Fields are separated by a comma (,). +The referenced field names have to exist on the ``targetEntity`` +class of the ``@ManyToMany`` or ``@OneToMany`` annotation. + +The semantics of this feature can be described as follows. + + +- ``@OrderBy`` acts as an implicit ORDER BY clause for the given + fields, that is appended to all the explicitly given ORDER BY + items. +- All collections of the ordered type are always retrieved in an + ordered fashion. +- To keep the database impact low, these implicit ORDER BY items + are only added to an DQL Query if the collection is fetch joined in + the DQL query. + +Given our previously defined example, the following would not add +ORDER BY, since g is not fetch joined: + +.. code-block:: sql + + SELECT u FROM User u JOIN u.groups g WHERE SIZE(g) > 10 + +However the following: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 + +...would internally be rewritten to: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name ASC + +You can reverse the order with an explicit DQL ORDER BY: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC + +...is internally rewritten to: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC + + diff --git a/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst b/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst new file mode 100644 index 00000000000..aef663f2a68 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst @@ -0,0 +1,90 @@ +Override Field Association Mappings In Subclasses +------------------------------------------------- + +Sometimes there is a need to persist entities but override all or part of the +mapping metadata. Sometimes also the mapping to override comes from entities +using traits where the traits have mapping metadata. +This tutorial explains how to override mapping metadata, +i.e. attributes and associations metadata in particular. The example here shows +the overriding of a class that uses a trait but is similar when extending a base +class as shown at the end of this tutorial. + +Suppose we have a class ExampleEntityWithOverride. This class uses trait ExampleTrait: + +.. code-block:: php + + `). diff --git a/vendor/doctrine/orm/docs/en/tutorials/pagination.rst b/vendor/doctrine/orm/docs/en/tutorials/pagination.rst new file mode 100644 index 00000000000..39713838751 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/pagination.rst @@ -0,0 +1,43 @@ +Pagination +========== + +.. versionadded:: 2.2 + +Starting with version 2.2 Doctrine ships with a Paginator for DQL queries. It +has a very simple API and implements the SPL interfaces ``Countable`` and +``IteratorAggregate``. + +.. code-block:: php + + createQuery($dql) + ->setFirstResult(0) + ->setMaxResults(100); + + $paginator = new Paginator($query, $fetchJoinCollection = true); + + $c = count($paginator); + foreach ($paginator as $post) { + echo $post->getHeadline() . "\n"; + } + +Paginating Doctrine queries is not as simple as you might think in the +beginning. If you have complex fetch-join scenarios with one-to-many or +many-to-many associations using the "default" LIMIT functionality of database +vendors is not sufficient to get the correct results. + +By default the pagination extension does the following steps to compute the +correct result: + +- Perform a Count query using `DISTINCT` keyword. +- Perform a Limit Subquery with `DISTINCT` to find all ids of the entity in from on the current page. +- Perform a WHERE IN query to get all results for the current page. + +This behavior is only necessary if you actually fetch join a to-many +collection. You can disable this behavior by setting the +``$fetchJoinCollection`` flag to ``false``; in that case only 2 instead of the 3 queries +described are executed. We hope to automate the detection for this in +the future. diff --git a/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst new file mode 100644 index 00000000000..3536a34f8ee --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst @@ -0,0 +1,298 @@ +Working with Indexed Associations +================================= + +.. note:: + + This feature is scheduled for version 2.1 of Doctrine and not included in the 2.0.x series. + +Doctrine 2 collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in +the first version of Doctrine keys retrieved from the database were always numerical unless ``INDEX BY`` +was used. Starting with Doctrine 2.1 you can index your collections by a value in the related entity. +This is a first step towards full ordered hashmap support through the Doctrine ORM. +The feature works like an implicit ``INDEX BY`` for the selected association but has several +downsides also: + +- You have to manage both the key and field if you want to change the index by field value. +- On each request the keys are regenerated from the field value not from the previous collection key. +- Values of the Index-By keys are never considered during persistence, it only exists for accessing purposes. +- Fields that are used for the index by feature **HAVE** to be unique in the database. The behavior for multiple entities + with the same index-by field value is undefined. + +As an example we will design a simple stock exchange list view. The domain consists of the entity ``Stock`` +and ``Market`` where each Stock has a symbol and is traded on a single market. Instead of having a numerical +list of stocks traded on a market they will be indexed by their symbol, which is unique across all markets. + +Mapping Indexed Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can map indexed associations by adding: + + * ``indexBy`` attribute to any ``@OneToMany`` or ``@ManyToMany`` annotation. + * ``index-by`` attribute to any ```` or ```` xml element. + * ``indexBy:`` key-value pair to any association defined in ``manyToMany:`` or ``oneToMany:`` YAML mapping files. + +The code and mappings for the Market entity looks like this: + +.. configuration-block:: + .. code-block:: php + + name = $name; + $this->stocks = new ArrayCollection(); + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function addStock(Stock $stock) + { + $this->stocks[$stock->getSymbol()] = $stock; + } + + public function getStock($symbol) + { + if (!isset($this->stocks[$symbol])) { + throw new \InvalidArgumentException("Symbol is not traded on this market."); + } + + return $this->stocks[$symbol]; + } + + public function getStocks() + { + return $this->stocks->toArray(); + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\StockExchange\Market: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type:string + oneToMany: + stocks: + targetEntity: Stock + mappedBy: market + indexBy: symbol + +Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol, +so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)`` +we pick a stock traded on the particular market by symbol. If this stock doesn't exist an exception is thrown. + +The ``Stock`` entity doesn't contain any special instructions that are new, but for completeness +here are the code and mappings for it: + +.. configuration-block:: + .. code-block:: php + + symbol = $symbol; + $this->market = $market; + $market->addStock($this); + } + + public function getSymbol() + { + return $this->symbol; + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\StockExchange\Stock: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + symbol: + type: string + manyToOne: + market: + targetEntity: Market + inversedBy: stocks + +Querying indexed associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that we defined the stocks collection to be indexed by symbol we can take a look at some code, +that makes use of the indexing. + +First we will populate our database with two example stocks traded on a single market: + +.. code-block:: php + + persist($market); + $em->persist($stock1); + $em->persist($stock2); + $em->flush(); + +This code is not particular interesting since the indexing feature is not yet used. In a new request we could +now query for the market: + +.. code-block:: php + + find("Doctrine\Tests\Models\StockExchange\Market", $marketId); + + // Access the stocks by symbol now: + $stock = $market->getStock($symbol); + + echo $stock->getSymbol(); // will print "AAPL" + +The implementation ``Market::addStock()`` in combination with ``indexBy`` allows to access the collection +consistently by the Stock symbol. It does not matter if Stock is managed by Doctrine or not. + +The same applies to DQL queries: The ``indexBy`` configuration acts as implicit "INDEX BY" to a join association. + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $marketId) + ->getSingleResult(); + + // Access the stocks by symbol now: + $stock = $market->getStock($symbol); + + echo $stock->getSymbol(); // will print "AAPL" + +If you want to use ``INDEX BY`` explicitly on an indexed association you are free to do so. Additionally +indexed associations also work with the ``Collection::slice()`` functionality, no matter if marked as +LAZY or EXTRA_LAZY. + +Outlook into the Future +~~~~~~~~~~~~~~~~~~~~~~~ + +For the inverse side of a many-to-many associations there will be a way to persist the keys and the order +as a third and fourth parameter into the join table. This feature is discussed in `DDC-213 `_ +This feature cannot be implemented for One-To-Many associations, because they are never the owning side. + diff --git a/vendor/doctrine/orm/doctrine-mapping.xsd b/vendor/doctrine/orm/doctrine-mapping.xsd new file mode 100644 index 00000000000..6a7e2a28483 --- /dev/null +++ b/vendor/doctrine/orm/doctrine-mapping.xsd @@ -0,0 +1,603 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 00000000000..e1667add231 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php @@ -0,0 +1,1120 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\ORM\Query\Parameter; +use Doctrine\ORM\Cache\QueryCacheKey; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +use Doctrine\ORM\Cache; +use Doctrine\ORM\Query\QueryException; + +/** + * Base contract for ORM queries. Base class for Query and NativeQuery. + * + * @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; + + /** + * The parameter map of this query. + * + * @var \Doctrine\Common\Collections\ArrayCollection + */ + protected $parameters; + + /** + * The user-specified ResultSetMapping to use. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + protected $_resultSetMapping; + + /** + * The entity manager used by this query object. + * + * @var EntityManagerInterface + */ + protected $_em; + + /** + * The map of query hints. + * + * @var array + */ + protected $_hints = array(); + + /** + * The hydration mode. + * + * @var integer + */ + protected $_hydrationMode = self::HYDRATE_OBJECT; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_queryCacheProfile; + + /** + * Whether or not expire the result cache. + * + * @var boolean + */ + protected $_expireResultCache = false; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_hydrationCacheProfile; + + /** + * Whether to use second level cache, if available. + * + * @var boolean + */ + protected $cacheable = false; + + /** + * @var boolean + */ + protected $hasCache = false; + + /** + * Second level cache region name. + * + * @var string|null + */ + protected $cacheRegion; + + /** + * Second level query cache mode. + * + * @var integer|null + */ + protected $cacheMode; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + protected $cacheLogger; + + /** + * @var integer + */ + protected $lifetime = 0; + + /** + * Initializes a new instance of a class derived from AbstractQuery. + * + * @param \Doctrine\ORM\EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->_em = $em; + $this->parameters = new ArrayCollection(); + $this->_hints = $em->getConfiguration()->getDefaultQueryHints(); + $this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled(); + + if ($this->hasCache) { + $this->cacheLogger = $em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheLogger(); + } + } + + /** + * + * Enable/disable second level query (result) caching for this query. + * + * @param boolean $cacheable + * + * @return static This query instance. + */ + public function setCacheable($cacheable) + { + $this->cacheable = (boolean) $cacheable; + + return $this; + } + + /** + * @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise. + */ + public function isCacheable() + { + return $this->cacheable; + } + + /** + * @param string $cacheRegion + * + * @return static This query instance. + */ + public function setCacheRegion($cacheRegion) + { + $this->cacheRegion = (string) $cacheRegion; + + return $this; + } + + /** + * Obtain the name of the second level query cache region in which query results will be stored + * + * @return The cache region name; NULL indicates the default region. + */ + public function getCacheRegion() + { + return $this->cacheRegion; + } + + /** + * @return boolean TRUE if the query cache and second level cache are enabled, FALSE otherwise. + */ + protected function isCacheEnabled() + { + return $this->cacheable && $this->hasCache; + } + + /** + * @return integer + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * Sets the life-time for this query into second level cache. + * + * @param integer $lifetime + * + * @return static This query instance. + */ + public function setLifetime($lifetime) + { + $this->lifetime = (integer) $lifetime; + + return $this; + } + + /** + * @return integer + */ + public function getCacheMode() + { + return $this->cacheMode; + } + + /** + * @param integer $cacheMode + * + * @return static This query instance. + */ + public function setCacheMode($cacheMode) + { + $this->cacheMode = (integer) $cacheMode; + + return $this; + } + + /** + * 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(); + + /** + * 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->parameters = new ArrayCollection(); + + $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints(); + } + + /** + * Get all defined parameters. + * + * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters. + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets a query parameter. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return Query\Parameter|null The value of the bound parameter, or NULL if not available. + */ + public function getParameter($key) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + return count($filteredParameters) ? $filteredParameters->first() : null; + } + + /** + * Sets a collection of query parameters. + * + * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters + * + * @return static This query instance. + */ + public function setParameters($parameters) + { + // BC compatibility with 2.3- + if (is_array($parameters)) { + $parameterCollection = new ArrayCollection(); + + foreach ($parameters as $key => $value) { + $parameterCollection->add(new Parameter($key, $value)); + } + + $parameters = $parameterCollection; + } + + $this->parameters = $parameters; + + return $this; + } + + /** + * Sets a query parameter. + * + * @param string|int $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $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 static This query instance. + */ + public function setParameter($key, $value, $type = null) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + if (count($filteredParameters)) { + $parameter = $filteredParameters->first(); + $parameter->setValue($value, $type); + + return $this; + } + + $this->parameters->add(new Parameter($key, $value, $type)); + + return $this; + } + + /** + * Processes an individual parameter value. + * + * @param mixed $value + * + * @return array + * + * @throws \Doctrine\ORM\ORMInvalidArgumentException + */ + public function processParameterValue($value) + { + if (is_scalar($value)) { + return $value; + } + + if ($value instanceof Collection) { + $value = $value->toArray(); + } + + if (is_array($value)) { + foreach ($value as $key => $paramValue) { + $paramValue = $this->processParameterValue($paramValue); + $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue; + } + + return $value; + } + + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + $value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value); + + if ($value === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + + if ($value instanceof Mapping\ClassMetadata) { + return $value->name; + } + + return $value; + } + + /** + * Sets the ResultSetMapping that should be used for hydration. + * + * @param \Doctrine\ORM\Query\ResultSetMapping $rsm + * + * @return static This query instance. + */ + public function setResultSetMapping(Query\ResultSetMapping $rsm) + { + $this->translateNamespaces($rsm); + $this->_resultSetMapping = $rsm; + + return $this; + } + + /** + * Gets the ResultSetMapping used for hydration. + * + * @return \Doctrine\ORM\Query\ResultSetMapping + */ + protected function getResultSetMapping() + { + return $this->_resultSetMapping; + } + + /** + * Allows to translate entity namespaces to full qualified names. + * + * @param Query\ResultSetMapping $rsm + * + * @return void + */ + private function translateNamespaces(Query\ResultSetMapping $rsm) + { + $translate = function ($alias) { + return $this->_em->getClassMetadata($alias)->getName(); + }; + + $rsm->aliasMap = array_map($translate, $rsm->aliasMap); + $rsm->declaringClasses = array_map($translate, $rsm->declaringClasses); + } + + /** + * 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 static This query instance. + */ + 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 static This query instance. + */ + 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 implicitly enables caching. + * + * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver + * + * @return static This query instance. + * + * @throws ORMException + */ + 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 static 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 static 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 static 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 static This query instance. + */ + 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 static 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(null, $hydrationMode = HYDRATE_OBJECT). + * + * @param int $hydrationMode + * + * @return array + */ + public function getResult($hydrationMode = self::HYDRATE_OBJECT) + { + return $this->execute(null, $hydrationMode); + } + + /** + * Gets the array of results for the query. + * + * Alias for execute(null, HYDRATE_ARRAY). + * + * @return array + */ + public function getArrayResult() + { + return $this->execute(null, self::HYDRATE_ARRAY); + } + + /** + * Gets the scalar results for the query. + * + * Alias for execute(null, HYDRATE_SCALAR). + * + * @return array + */ + public function getScalarResult() + { + return $this->execute(null, self::HYDRATE_SCALAR); + } + + /** + * Get exactly one result or null. + * + * @param int $hydrationMode + * + * @return mixed + * + * @throws NonUniqueResultException + */ + public function getOneOrNullResult($hydrationMode = null) + { + try { + $result = $this->execute(null, $hydrationMode); + } catch (NoResultException $e) { + return null; + } + + + 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(null, $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 NonUniqueResultException If the query result is not unique. + * @throws NoResultException If the query returned no result. + */ + 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 static This query instance. + */ + 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; + } + + /** + * Check if the query has a hint + * + * @param string $name The name of the hint + * + * @return bool False if the query does not have any hint + */ + public function hasHint($name) + { + return isset($this->_hints[$name]); + } + + /** + * 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 ArrayCollection|array|null $parameters The query parameters. + * @param integer|null $hydrationMode The hydration mode to use. + * + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate($parameters = null, $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ( ! empty($parameters)) { + $this->setParameters($parameters); + } + + $rsm = $this->getResultSetMapping(); + $stmt = $this->_doExecute(); + + return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints); + } + + /** + * Executes the query. + * + * @param ArrayCollection|array|null $parameters Query parameters. + * @param integer|null $hydrationMode Processing mode to be used during the hydration process. + * + * @return mixed + */ + public function execute($parameters = null, $hydrationMode = null) + { + if ($this->cacheable && $this->isCacheEnabled()) { + return $this->executeUsingQueryCache($parameters, $hydrationMode); + } + + return $this->executeIgnoreQueryCache($parameters, $hydrationMode); + } + + /** + * Execute query ignoring second level cache. + * + * @param ArrayCollection|array|null $parameters + * @param integer|null $hydrationMode + * + * @return mixed + */ + private function executeIgnoreQueryCache($parameters = null, $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ( ! empty($parameters)) { + $this->setParameters($parameters); + } + + $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; + } + + $rsm = $this->getResultSetMapping(); + $data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt, $rsm, $this->_hints); + + $setCacheEntry($data); + + return $data; + } + + /** + * Load from second level cache or executes the query and put into cache. + * + * @param ArrayCollection|array|null $parameters + * @param integer|null $hydrationMode + * + * @return mixed + */ + private function executeUsingQueryCache($parameters = null, $hydrationMode = null) + { + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($this->getHash(), $this->lifetime, $this->cacheMode ?: Cache::MODE_NORMAL); + $queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion); + $result = $queryCache->get($querykey, $rsm, $this->_hints); + + if ($result !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $querykey); + } + + return $result; + } + + $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode); + $cached = $queryCache->put($querykey, $rsm, $result, $this->_hints); + + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $querykey); + + if ($cached) { + $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $querykey); + } + } + + return $result; + } + + /** + * 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() + { + $parameters = array(); + + foreach ($this->getParameters() as $parameter) { + $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue()); + } + + $sql = $this->getSQL(); + $queryCacheProfile = $this->getHydrationCacheProfile(); + $hints = $this->getHints(); + $hints['hydrationMode'] = $this->getHydrationMode(); + + ksort($hints); + + return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints); + } + + /** + * Set the result cache id to use to store the result set cache entry. + * If this is not explicitly set by the developer then a hash is automatically + * generated for you. + * + * @param string $id + * + * @return static 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->parameters = new ArrayCollection(); + + $this->_hints = array(); + $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints(); + } + + /** + * Generates a string of currently query to use for the cache second level cache. + * + * @return string + */ + protected function getHash() + { + $query = $this->getSQL(); + $hints = $this->getHints(); + $params = array_map(function(Parameter $parameter) { + // Small optimization + // Does not invoke processParameterValue for scalar values + if (is_scalar($value = $parameter->getValue())) { + return $value; + } + + return $this->processParameterValue($value); + }, $this->parameters->getValues()); + + ksort($hints); + + return sha1($query . '-' . serialize($params) . '-' . serialize($hints)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache.php new file mode 100644 index 00000000000..3da7c05ef4e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Provides an API for querying/managing the second level cache regions. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface Cache +{ + const DEFAULT_QUERY_REGION_NAME = 'query_cache_region'; + + const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region'; + + /** + * May read items from the cache, but will not add items. + */ + const MODE_GET = 1; + + /** + * Will never read items from the cache, + * but will add items to the cache as it reads them from the database. + */ + const MODE_PUT = 2; + + /** + * May read items from the cache, and add items to the cache. + */ + const MODE_NORMAL = 3; + + /** + * The query will never read items from the cache, + * but will refresh items to the cache as it reads them from the database. + */ + const MODE_REFRESH = 4; + + /** + * @param string $className The entity class. + * + * @return \Doctrine\ORM\Cache\Region|null + */ + public function getEntityCacheRegion($className); + + /** + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * + * @return \Doctrine\ORM\Cache\Region|null + */ + public function getCollectionCacheRegion($className, $association); + + /** + * Determine whether the cache contains data for the given entity "instance". + * + * @param string $className The entity class. + * @param mixed $identifier The entity identifier + * + * @return boolean true if the underlying cache contains corresponding data; false otherwise. + */ + public function containsEntity($className, $identifier); + + /** + * Evicts the entity data for a particular entity "instance". + * + * @param string $className The entity class. + * @param mixed $identifier The entity identifier. + * + * @return void + */ + public function evictEntity($className, $identifier); + + /** + * Evicts all entity data from the given region. + * + * @param string $className The entity metadata. + * + * @return void + */ + public function evictEntityRegion($className); + + /** + * Evict data from all entity regions. + * + * @return void + */ + public function evictEntityRegions(); + + /** + * Determine whether the cache contains data for the given collection. + * + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * @param mixed $ownerIdentifier The identifier of the owning entity. + * + * @return boolean true if the underlying cache contains corresponding data; false otherwise. + */ + public function containsCollection($className, $association, $ownerIdentifier); + + /** + * Evicts the cache data for the given identified collection instance. + * + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * @param mixed $ownerIdentifier The identifier of the owning entity. + * + * @return void + */ + public function evictCollection($className, $association, $ownerIdentifier); + + /** + * Evicts all entity data from the given region. + * + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * + * @return void + */ + public function evictCollectionRegion($className, $association); + + /** + * Evict data from all collection regions. + * + * @return void + */ + public function evictCollectionRegions(); + + /** + * Determine whether the cache contains data for the given query. + * + * @param string $regionName The cache name given to the query. + * + * @return boolean true if the underlying cache contains corresponding data; false otherwise. + */ + public function containsQuery($regionName); + + /** + * Evicts all cached query results under the given name, or default query cache if the region name is NULL. + * + * @param string|null $regionName The cache name associated to the queries being cached. + */ + public function evictQueryRegion($regionName = null); + + /** + * Evict data from all query regions. + * + * @return void + */ + public function evictQueryRegions(); + + /** + * Get query cache by region name or create a new one if none exist. + * + * @param string|null $regionName Query cache region name, or default query cache if the region name is NULL. + * + * @return \Doctrine\ORM\Cache\QueryCache The Query Cache associated with the region name. + */ + public function getQueryCache($regionName = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/AssociationCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/AssociationCacheEntry.php new file mode 100644 index 00000000000..3aee1096bfc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/AssociationCacheEntry.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Association cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class AssociationCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The entity identifier + */ + public $identifier; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The entity class name + */ + public $class; + + /** + * @param string $class The entity class. + * @param array $identifier The entity identifier. + */ + public function __construct($class, array $identifier) + { + $this->class = $class; + $this->identifier = $identifier; + } + + /** + * Creates a new AssociationCacheEntry + * + * This method allow Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + */ + public static function __set_state(array $values) + { + return new self($values['class'], $values['identifier']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheConfiguration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheConfiguration.php new file mode 100644 index 00000000000..4891cac4909 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheConfiguration.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Cache\Logging\CacheLogger; + +/** + * Configuration container for second-level cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CacheConfiguration +{ + /** + * @var \Doctrine\ORM\Cache\CacheFactory|null + */ + private $cacheFactory; + + /** + * @var \Doctrine\ORM\Cache\RegionsConfiguration|null + */ + private $regionsConfig; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + private $cacheLogger; + + /** + * @var \Doctrine\ORM\Cache\QueryCacheValidator|null + */ + private $queryValidator; + + /** + * @return \Doctrine\ORM\Cache\CacheFactory|null + */ + public function getCacheFactory() + { + return $this->cacheFactory; + } + + /** + * @param \Doctrine\ORM\Cache\CacheFactory $factory + * + * @return void + */ + public function setCacheFactory(CacheFactory $factory) + { + $this->cacheFactory = $factory; + } + + /** + * @return \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + public function getCacheLogger() + { + return $this->cacheLogger; + } + + /** + * @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger + */ + public function setCacheLogger(CacheLogger $logger) + { + $this->cacheLogger = $logger; + } + + /** + * @return \Doctrine\ORM\Cache\RegionsConfiguration + */ + public function getRegionsConfiguration() + { + if ($this->regionsConfig === null) { + $this->regionsConfig = new RegionsConfiguration(); + } + + return $this->regionsConfig; + } + + /** + * @param \Doctrine\ORM\Cache\RegionsConfiguration $regionsConfig + */ + public function setRegionsConfiguration(RegionsConfiguration $regionsConfig) + { + $this->regionsConfig = $regionsConfig; + } + + /** + * @return \Doctrine\ORM\Cache\QueryCacheValidator + */ + public function getQueryValidator() + { + if ($this->queryValidator === null) { + $this->queryValidator = new TimestampQueryCacheValidator(); + } + + return $this->queryValidator; + } + + /** + * @param \Doctrine\ORM\Cache\QueryCacheValidator $validator + */ + public function setQueryValidator(QueryCacheValidator $validator) + { + $this->queryValidator = $validator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheEntry.php new file mode 100644 index 00000000000..c34b0ff9b00 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheEntry.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache entry interface + * + * IMPORTANT NOTE: + * + * Fields of classes that implement CacheEntry are public for performance reason. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CacheEntry +{ + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheException.php new file mode 100644 index 00000000000..5b548fe5ec7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheException.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\ORMException; + +/** + * Exception for cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CacheException extends ORMException +{ + /** + * @param string $sourceEntity + * @param string $fieldName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function updateReadOnlyCollection($sourceEntity, $fieldName) + { + return new self(sprintf('Cannot update a readonly collection "%s#%s"', $sourceEntity, $fieldName)); + } + + /** + * @param string $entityName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function updateReadOnlyEntity($entityName) + { + return new self(sprintf('Cannot update a readonly entity "%s"', $entityName)); + } + + /** + * @param string $entityName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function nonCacheableEntity($entityName) + { + return new self(sprintf('Entity "%s" not configured as part of the second-level cache.', $entityName)); + } + + /** + * @param string $entityName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function nonCacheableEntityAssociation($entityName, $field) + { + return new self(sprintf('Entity association field "%s#%s" not configured as part of the second-level cache.', $entityName, $field)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheFactory.php new file mode 100644 index 00000000000..b915100623a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheFactory.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * Contract for building second level cache regions components. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CacheFactory +{ + /** + * Build an entity persister for the given entity metadata. + * + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister that will be cached. + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * + * @return \Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister + */ + public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata); + + /** + * Build a collection persister for the given relation mapping. + * + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached. + * @param array $mapping The association mapping. + * + * @return \Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister + */ + public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping); + + /** + * Build a query cache based on the given region name + * + * @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager. + * @param string $regionName The region name. + * + * @return \Doctrine\ORM\Cache\QueryCache The built query cache. + */ + public function buildQueryCache(EntityManagerInterface $em, $regionName = null); + + /** + * Build an entity hydrator + * + * @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager. + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * + * @return \Doctrine\ORM\Cache\EntityHydrator The built entity hydrator. + */ + public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata); + + /** + * Build a collection hydrator + * + * @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager. + * @param array $mapping The association mapping. + * + * @return \Doctrine\ORM\Cache\CollectionHydrator The built collection hydrator. + */ + public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping); + + /** + * Build a cache region + * + * @param array $cache The cache configuration. + * + * @return \Doctrine\ORM\Cache\Region The cache region. + */ + public function getRegion(array $cache); + + /** + * Build timestamp cache region + * + * @return \Doctrine\ORM\Cache\TimestampRegion The timestamp region. + */ + public function getTimestampRegion(); + + /** + * Build \Doctrine\ORM\Cache + * + * @param EntityManagerInterface $entityManager + * + * @return \Doctrine\ORM\Cache + */ + public function createCache(EntityManagerInterface $entityManager); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheKey.php new file mode 100644 index 00000000000..1641c9900dc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheKey.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines entity / collection / query key to be stored in the cache region. + * Allows multiple roles to be stored in the same cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +abstract class CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string Unique identifier + */ + public $hash; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheEntry.php new file mode 100644 index 00000000000..6c634ce311d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheEntry.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Collection cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CollectionCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var CacheKey[] The list of entity identifiers hold by the collection + */ + public $identifiers; + + /** + * @param CacheKey[] $identifiers List of entity identifiers hold by the collection + */ + public function __construct(array $identifiers) + { + $this->identifiers = $identifiers; + } + + /** + * Creates a new CollectionCacheEntry + * + * This method allows for Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + * + * @return self + */ + public static function __set_state(array $values) + { + return new self($values['identifiers']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheKey.php new file mode 100644 index 00000000000..6b631455000 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheKey.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines entity collection roles to be stored in the cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CollectionCacheKey extends CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The owner entity identifier + */ + public $ownerIdentifier; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The owner entity class + */ + public $entityClass; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The association name + */ + public $association; + + /** + * @param string $entityClass The entity class. + * @param string $association The field name that represents the association. + * @param array $ownerIdentifier The identifier of the owning entity. + */ + public function __construct($entityClass, $association, array $ownerIdentifier) + { + ksort($ownerIdentifier); + + $this->ownerIdentifier = $ownerIdentifier; + $this->entityClass = (string) $entityClass; + $this->association = (string) $association; + $this->hash = str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionHydrator.php new file mode 100644 index 00000000000..04cdde16268 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionHydrator.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Hydrator cache entry for collections + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CollectionHydrator +{ + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key. + * @param array|\Doctrine\Common\Collections\Collection $collection The collection. + * + * @return \Doctrine\ORM\Cache\CollectionCacheEntry + */ + public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection); + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The owning entity metadata. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key. + * @param \Doctrine\ORM\Cache\CollectionCacheEntry $entry The cached collection entry. + * @param \Doctrine\ORM\PersistentCollection $collection The collection to load the cache into. + * + * @return array + */ + public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/ConcurrentRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/ConcurrentRegion.php new file mode 100644 index 00000000000..12da6d615ad --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/ConcurrentRegion.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines contract for concurrently managed data region. + * It should be able to lock an specific cache entry in an atomic operation. + * + * When a entry is locked another process should not be able to read or write the entry. + * All evict operation should not consider locks, even though an entry is locked evict should be able to delete the entry and its lock. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface ConcurrentRegion extends Region +{ + /** + * Attempts to read lock the mapping for the given key. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to lock. + * + * @return \Doctrine\ORM\Cache\Lock A lock instance or NULL if the lock already exists. + * + * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. + */ + public function lock(CacheKey $key); + + /** + * Attempts to read unlock the mapping for the given key. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to unlock. + * @param \Doctrine\ORM\Cache\Lock $lock The lock previously obtained from {@link readLock} + * + * @return void + * + * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. + */ + public function unlock(CacheKey $key, Lock $lock); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCache.php new file mode 100644 index 00000000000..78a22a80f1d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCache.php @@ -0,0 +1,342 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Cache; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\ORMInvalidArgumentException; + +/** + * Provides an API for querying/managing the second level cache regions. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultCache implements Cache +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * @var \Doctrine\ORM\Cache\CacheFactory + */ + private $cacheFactory; + + /** + * @var \Doctrine\ORM\Cache\QueryCache[] + */ + private $queryCaches = array(); + + /** + * @var \Doctrine\ORM\Cache\QueryCache + */ + private $defaultQueryCache; + + /** + * {@inheritdoc} + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->cacheFactory = $em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheFactory(); + } + + /** + * {@inheritdoc} + */ + public function getEntityCacheRegion($className) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return null; + } + + return $persister->getCacheRegion(); + } + + /** + * {@inheritdoc} + */ + public function getCollectionCacheRegion($className, $association) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return null; + } + + return $persister->getCacheRegion(); + } + + /** + * {@inheritdoc} + */ + public function containsEntity($className, $identifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return false; + } + + return $persister->getCacheRegion()->contains($this->buildEntityCacheKey($metadata, $identifier)); + } + + /** + * {@inheritdoc} + */ + public function evictEntity($className, $identifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evict($this->buildEntityCacheKey($metadata, $identifier)); + } + + /** + * {@inheritdoc} + */ + public function evictEntityRegion($className) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evictAll(); + } + + /** + * {@inheritdoc} + */ + public function evictEntityRegions() + { + $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); + + foreach ($metadatas as $metadata) { + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + continue; + } + + $persister->getCacheRegion()->evictAll(); + } + } + + /** + * {@inheritdoc} + */ + public function containsCollection($className, $association, $ownerIdentifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return false; + } + + return $persister->getCacheRegion()->contains($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier)); + } + + /** + * {@inheritdoc} + */ + public function evictCollection($className, $association, $ownerIdentifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evict($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier)); + } + + /** + * {@inheritdoc} + */ + public function evictCollectionRegion($className, $association) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evictAll(); + } + + /** + * {@inheritdoc} + */ + public function evictCollectionRegions() + { + $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); + + foreach ($metadatas as $metadata) { + + foreach ($metadata->associationMappings as $association) { + + if ( ! $association['type'] & ClassMetadata::TO_MANY) { + continue; + } + + $persister = $this->uow->getCollectionPersister($association); + + if ( ! ($persister instanceof CachedPersister)) { + continue; + } + + $persister->getCacheRegion()->evictAll(); + } + } + } + + /** + * {@inheritdoc} + */ + public function containsQuery($regionName) + { + return isset($this->queryCaches[$regionName]); + } + + /** + * {@inheritdoc} + */ + public function evictQueryRegion($regionName = null) + { + if ($regionName === null && $this->defaultQueryCache !== null) { + $this->defaultQueryCache->clear(); + + return; + } + + if (isset($this->queryCaches[$regionName])) { + $this->queryCaches[$regionName]->clear(); + } + } + + /** + * {@inheritdoc} + */ + public function evictQueryRegions() + { + $this->getQueryCache()->clear(); + + foreach ($this->queryCaches as $queryCache) { + $queryCache->clear(); + } + } + + /** + * {@inheritdoc} + */ + public function getQueryCache($regionName = null) + { + if ($regionName === null) { + return $this->defaultQueryCache ?: + $this->defaultQueryCache = $this->cacheFactory->buildQueryCache($this->em); + } + + if ( ! isset($this->queryCaches[$regionName])) { + $this->queryCaches[$regionName] = $this->cacheFactory->buildQueryCache($this->em, $regionName); + } + + return $this->queryCaches[$regionName]; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param mixed $identifier The entity identifier. + * + * @return \Doctrine\ORM\Cache\EntityCacheKey + */ + private function buildEntityCacheKey(ClassMetadata $metadata, $identifier) + { + if ( ! is_array($identifier)) { + $identifier = $this->toIdentifierArray($metadata, $identifier); + } + + return new EntityCacheKey($metadata->rootEntityName, $identifier); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $association The field name that represents the association. + * @param mixed $ownerIdentifier The identifier of the owning entity. + * + * @return \Doctrine\ORM\Cache\CollectionCacheKey + */ + private function buildCollectionCacheKey(ClassMetadata $metadata, $association, $ownerIdentifier) + { + if ( ! is_array($ownerIdentifier)) { + $ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier);; + } + + return new CollectionCacheKey($metadata->rootEntityName, $association, $ownerIdentifier); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param mixed $identifier The entity identifier. + * + * @return array + */ + private function toIdentifierArray(ClassMetadata $metadata, $identifier) + { + if (is_object($identifier) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($identifier))) { + $identifier = $this->uow->getSingleIdentifierValue($identifier); + + if ($identifier === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + + return array($metadata->identifier[0] => $identifier); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php new file mode 100644 index 00000000000..82843d35933 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php @@ -0,0 +1,254 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\Common\Cache\Cache as CacheAdapter; +use Doctrine\Common\Cache\CacheProvider; +use Doctrine\Common\Cache\MultiGetCache; +use Doctrine\ORM\Cache; +use Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister; +use Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister; +use Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister; +use Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister; +use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister; +use Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\Region\DefaultMultiGetRegion; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache\Region\FileLockRegion; +use Doctrine\ORM\Cache\Region\UpdateTimestampCache; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultCacheFactory implements CacheFactory +{ + /** + * @var CacheAdapter + */ + private $cache; + + /** + * @var \Doctrine\ORM\Cache\RegionsConfiguration + */ + private $regionsConfig; + + /** + * @var \Doctrine\ORM\Cache\TimestampRegion|null + */ + private $timestampRegion; + + /** + * @var \Doctrine\ORM\Cache\Region[] + */ + private $regions = array(); + + /** + * @var string|null + */ + private $fileLockRegionDirectory; + + /** + * @param RegionsConfiguration $cacheConfig + * @param CacheAdapter $cache + */ + public function __construct(RegionsConfiguration $cacheConfig, CacheAdapter $cache) + { + $this->cache = $cache; + $this->regionsConfig = $cacheConfig; + } + + /** + * @param string $fileLockRegionDirectory + */ + public function setFileLockRegionDirectory($fileLockRegionDirectory) + { + $this->fileLockRegionDirectory = (string) $fileLockRegionDirectory; + } + + /** + * @return string + */ + public function getFileLockRegionDirectory() + { + return $this->fileLockRegionDirectory; + } + + /** + * @param \Doctrine\ORM\Cache\Region $region + */ + public function setRegion(Region $region) + { + $this->regions[$region->getName()] = $region; + } + + /** + * @param \Doctrine\ORM\Cache\TimestampRegion $region + */ + public function setTimestampRegion(TimestampRegion $region) + { + $this->timestampRegion = $region; + } + + /** + * {@inheritdoc} + */ + public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata) + { + $region = $this->getRegion($metadata->cache); + $usage = $metadata->cache['usage']; + + if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) { + return new ReadOnlyCachedEntityPersister($persister, $region, $em, $metadata); + } + + if ($usage === ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE) { + return new NonStrictReadWriteCachedEntityPersister($persister, $region, $em, $metadata); + } + + if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) { + return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata); + } + + throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage)); + } + + /** + * {@inheritdoc} + */ + public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping) + { + $usage = $mapping['cache']['usage']; + $region = $this->getRegion($mapping['cache']); + + if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) { + return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping); + } + + if ($usage === ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE) { + return new NonStrictReadWriteCachedCollectionPersister($persister, $region, $em, $mapping); + } + + if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) { + return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping); + } + + throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage)); + } + + /** + * {@inheritdoc} + */ + public function buildQueryCache(EntityManagerInterface $em, $regionName = null) + { + return new DefaultQueryCache( + $em, + $this->getRegion( + array( + 'region' => $regionName ?: Cache::DEFAULT_QUERY_REGION_NAME, + 'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE + ) + ) + ); + } + + /** + * {@inheritdoc} + */ + public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping) + { + return new DefaultCollectionHydrator($em); + } + + /** + * {@inheritdoc} + */ + public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata) + { + return new DefaultEntityHydrator($em); + } + + /** + * {@inheritdoc} + */ + public function getRegion(array $cache) + { + if (isset($this->regions[$cache['region']])) { + return $this->regions[$cache['region']]; + } + + $cacheAdapter = clone $this->cache; + + if ($cacheAdapter instanceof CacheProvider) { + $cacheAdapter->setNamespace($cache['region']); + } + + $name = $cache['region']; + $lifetime = $this->regionsConfig->getLifetime($cache['region']); + + $region = ($cacheAdapter instanceof MultiGetCache) + ? new DefaultMultiGetRegion($name, $cacheAdapter, $lifetime) + : new DefaultRegion($name, $cacheAdapter, $lifetime); + + if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) { + + if ( ! $this->fileLockRegionDirectory) { + throw new \LogicException( + 'If you what to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, ' . + 'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you what to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). ' + ); + } + + $directory = $this->fileLockRegionDirectory . DIRECTORY_SEPARATOR . $cache['region']; + $region = new FileLockRegion($region, $directory, $this->regionsConfig->getLockLifetime($cache['region'])); + } + + return $this->regions[$cache['region']] = $region; + } + + /** + * {@inheritdoc} + */ + public function getTimestampRegion() + { + if ($this->timestampRegion === null) { + $name = Cache::DEFAULT_TIMESTAMP_REGION_NAME; + $lifetime = $this->regionsConfig->getLifetime($name); + + $this->timestampRegion = new UpdateTimestampCache($name, clone $this->cache, $lifetime); + } + + return $this->timestampRegion; + } + + /** + * {@inheritdoc} + */ + public function createCache(EntityManagerInterface $em) + { + return new DefaultCache($em); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php new file mode 100644 index 00000000000..f6583cbd2ca --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Query; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Default hydrator cache for collections + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultCollectionHydrator implements CollectionHydrator +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * @var array + */ + private static $hints = array(Query::HINT_CACHE_ENABLED => true); + + /** + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + } + + /** + * {@inheritdoc} + */ + public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection) + { + $data = array(); + + foreach ($collection as $index => $entity) { + $data[$index] = new EntityCacheKey($metadata->name, $this->uow->getEntityIdentifier($entity)); + } + return new CollectionCacheEntry($data); + } + + /** + * {@inheritdoc} + */ + public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection) + { + $assoc = $metadata->associationMappings[$key->association]; + /* @var $targetPersister \Doctrine\ORM\Cache\Persister\CachedPersister */ + $targetPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $targetRegion = $targetPersister->getCacheRegion(); + $list = array(); + + $entityEntries = $targetRegion->getMultiple($entry); + + if ($entityEntries === null) { + return null; + } + + /* @var $entityEntries \Doctrine\ORM\Cache\EntityCacheEntry[] */ + foreach ($entityEntries as $index => $entityEntry) { + $list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints); + } + + array_walk($list, function($entity, $index) use ($collection) { + $collection->hydrateSet($index, $entity); + }); + + $this->uow->hydrationComplete(); + + return $list; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php new file mode 100644 index 00000000000..70fd1f39a68 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php @@ -0,0 +1,192 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\Common\Util\ClassUtils; + +use Doctrine\ORM\Query; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Utility\IdentifierFlattener; + +/** + * Default hydrator cache for entities + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultEntityHydrator implements EntityHydrator +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * @var array + */ + private static $hints = array(Query::HINT_CACHE_ENABLED => true); + + /** + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory()); + } + + /** + * {@inheritdoc} + */ + public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity) + { + $data = $this->uow->getOriginalEntityData($entity); + $data = array_merge($data, $metadata->getIdentifierValues($entity)); // why update has no identifier values ? + + foreach ($metadata->associationMappings as $name => $assoc) { + + if ( ! isset($data[$name])) { + continue; + } + + if (! ($assoc['type'] & ClassMetadata::TO_ONE)) { + unset($data[$name]); + continue; + } + + if ( ! isset($assoc['cache'])) { + $targetClassMetadata = $this->em->getClassMetadata($assoc['targetEntity']); + $associationIds = $this->identifierFlattener->flattenIdentifier($targetClassMetadata, $targetClassMetadata->getIdentifierValues($data[$name])); + unset($data[$name]); + + foreach ($associationIds as $fieldName => $fieldValue) { + + if (isset($targetClassMetadata->associationMappings[$fieldName])){ + $targetAssoc = $targetClassMetadata->associationMappings[$fieldName]; + + foreach($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) { + + if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) { + $data[$localColumn] = $fieldValue; + } + } + }else{ + $data[$assoc['targetToSourceKeyColumns'][$targetClassMetadata->columnNames[$fieldName]]] = $fieldValue; + } + } + + continue; + } + + if ( ! isset($assoc['id'])) { + $targetClass = ClassUtils::getClass($data[$name]); + $targetId = $this->uow->getEntityIdentifier($data[$name]); + $data[$name] = new AssociationCacheEntry($targetClass, $targetId); + + continue; + } + + // handle association identifier + $targetId = is_object($data[$name]) && $this->uow->isInIdentityMap($data[$name]) + ? $this->uow->getEntityIdentifier($data[$name]) + : $data[$name]; + + // @TODO - fix it ! + // handle UnitOfWork#createEntity hash generation + if ( ! is_array($targetId)) { + + $data[reset($assoc['joinColumnFieldNames'])] = $targetId; + + $targetEntity = $this->em->getClassMetadata($assoc['targetEntity']); + $targetId = array($targetEntity->identifier[0] => $targetId); + } + + $data[$name] = new AssociationCacheEntry($assoc['targetEntity'], $targetId); + } + + return new EntityCacheEntry($metadata->name, $data); + } + + /** + * {@inheritdoc} + */ + public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null) + { + $data = $entry->data; + $hints = self::$hints; + + if ($entity !== null) { + $hints[Query::HINT_REFRESH] = true; + $hints[Query::HINT_REFRESH_ENTITY] = $entity; + } + + foreach ($metadata->associationMappings as $name => $assoc) { + if ( ! isset($assoc['cache']) || ! isset($data[$name])) { + continue; + } + + $assocClass = $data[$name]->class; + $assocId = $data[$name]->identifier; + $isEagerLoad = ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ($assoc['type'] === ClassMetadata::ONE_TO_ONE && ! $assoc['isOwningSide'])); + + if ( ! $isEagerLoad) { + $data[$name] = $this->em->getReference($assocClass, $assocId); + + continue; + } + + $assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId); + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocRegion = $assocPersister->getCacheRegion(); + $assocEntry = $assocRegion->get($assocKey); + + if ($assocEntry === null) { + return null; + } + + $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), $hints); + } + + if ($entity !== null) { + $this->uow->registerManaged($entity, $key->identifier, $data); + } + + $result = $this->uow->createEntity($entry->class, $data, $hints); + + $this->uow->hydrationComplete(); + + return $result; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php new file mode 100644 index 00000000000..87bb1366362 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php @@ -0,0 +1,342 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\Common\Proxy\Proxy; +use Doctrine\ORM\Cache; +use Doctrine\ORM\Query; + +/** + * Default query cache implementation. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultQueryCache implements QueryCache +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * @var \Doctrine\ORM\Cache\Region + */ + private $region; + + /** + * @var \Doctrine\ORM\Cache\QueryCacheValidator + */ + private $validator; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger + */ + protected $cacheLogger; + + /** + * @var array + */ + private static $hints = array(Query::HINT_CACHE_ENABLED => true); + + /** + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Cache\Region $region The query region. + */ + public function __construct(EntityManagerInterface $em, Region $region) + { + $cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration(); + + $this->em = $em; + $this->region = $region; + $this->uow = $em->getUnitOfWork(); + $this->cacheLogger = $cacheConfig->getCacheLogger(); + $this->validator = $cacheConfig->getQueryValidator(); + } + + /** + * {@inheritdoc} + */ + public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = array()) + { + if ( ! ($key->cacheMode & Cache::MODE_GET)) { + return null; + } + + $entry = $this->region->get($key); + + if ( ! $entry instanceof QueryCacheEntry) { + return null; + } + + if ( ! $this->validator->isValid($key, $entry)) { + $this->region->evict($key); + + return null; + } + + $result = array(); + $entityName = reset($rsm->aliasMap); + $hasRelation = ( ! empty($rsm->relationMap)); + $persister = $this->uow->getEntityPersister($entityName); + $region = $persister->getCacheRegion(); + $regionName = $region->getName(); + + // @TODO - move to cache hydration component + foreach ($entry->result as $index => $entry) { + + if (($entityEntry = $region->get($entityKey = new EntityCacheKey($entityName, $entry['identifier']))) === null) { + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheMiss($regionName, $entityKey); + } + + return null; + } + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheHit($regionName, $entityKey); + } + + if ( ! $hasRelation) { + + $result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints); + + continue; + } + + $data = $entityEntry->data; + + foreach ($entry['associations'] as $name => $assoc) { + + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocRegion = $assocPersister->getCacheRegion(); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + + if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assoc['targetEntity'], $assoc['identifier']))) === null) { + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey); + } + + $this->uow->hydrationComplete(); + + return null; + } + + $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints); + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey); + } + + continue; + } + + if ( ! isset($assoc['list']) || empty($assoc['list'])) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $collection = new PersistentCollection($this->em, $targetClass, new ArrayCollection()); + + foreach ($assoc['list'] as $assocIndex => $assocId) { + + if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId))) === null) { + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey); + } + + $this->uow->hydrationComplete(); + + return null; + } + + $element = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints); + + $collection->hydrateSet($assocIndex, $element); + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey); + } + } + + $data[$name] = $collection; + + $collection->setInitialized(true); + } + + $result[$index] = $this->uow->createEntity($entityEntry->class, $data, self::$hints); + } + + $this->uow->hydrationComplete(); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = array()) + { + if ($rsm->scalarMappings) { + throw new CacheException("Second level cache does not support scalar results."); + } + + if (count($rsm->entityMappings) > 1) { + throw new CacheException("Second level cache does not support multiple root entities."); + } + + if ( ! $rsm->isSelect) { + throw new CacheException("Second-level cache query supports only select statements."); + } + + if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD]) && $hints[Query::HINT_FORCE_PARTIAL_LOAD]) { + throw new CacheException("Second level cache does not support partial entities."); + } + + if ( ! ($key->cacheMode & Cache::MODE_PUT)) { + return false; + } + + $data = array(); + $entityName = reset($rsm->aliasMap); + $hasRelation = ( ! empty($rsm->relationMap)); + $metadata = $this->em->getClassMetadata($entityName); + $persister = $this->uow->getEntityPersister($entityName); + + if ( ! ($persister instanceof CachedPersister)) { + throw CacheException::nonCacheableEntity($entityName); + } + + $region = $persister->getCacheRegion(); + + foreach ($result as $index => $entity) { + $identifier = $this->uow->getEntityIdentifier($entity); + $data[$index]['identifier'] = $identifier; + $data[$index]['associations'] = array(); + + if (($key->cacheMode & Cache::MODE_REFRESH) || ! $region->contains($entityKey = new EntityCacheKey($entityName, $identifier))) { + // Cancel put result if entity put fail + if ( ! $persister->storeEntityCache($entity, $entityKey)) { + return false; + } + } + + if ( ! $hasRelation) { + continue; + } + + // @TODO - move to cache hydration components + foreach ($rsm->relationMap as $name) { + $assoc = $metadata->associationMappings[$name]; + + if (($assocValue = $metadata->getFieldValue($entity, $name)) === null || $assocValue instanceof Proxy) { + continue; + } + + if ( ! isset($assoc['cache'])) { + throw CacheException::nonCacheableEntityAssociation($entityName, $name); + } + + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocRegion = $assocPersister->getCacheRegion(); + $assocMetadata = $assocPersister->getClassMetadata(); + + // Handle *-to-one associations + if ($assoc['type'] & ClassMetadata::TO_ONE) { + + $assocIdentifier = $this->uow->getEntityIdentifier($assocValue); + + if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier))) { + + // Cancel put result if association entity put fail + if ( ! $assocPersister->storeEntityCache($assocValue, $entityKey)) { + return false; + } + } + + $data[$index]['associations'][$name] = array( + 'targetEntity' => $assocMetadata->rootEntityName, + 'identifier' => $assocIdentifier, + 'type' => $assoc['type'] + ); + + continue; + } + + // Handle *-to-many associations + $list = array(); + + foreach ($assocValue as $assocItemIndex => $assocItem) { + $assocIdentifier = $this->uow->getEntityIdentifier($assocItem); + + if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier))) { + + // Cancel put result if entity put fail + if ( ! $assocPersister->storeEntityCache($assocItem, $entityKey)) { + return false; + } + } + + $list[$assocItemIndex] = $assocIdentifier; + } + + $data[$index]['associations'][$name] = array( + 'targetEntity' => $assocMetadata->rootEntityName, + 'type' => $assoc['type'], + 'list' => $list, + ); + } + } + + return $this->region->put($key, new QueryCacheEntry($data)); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->region->evictAll(); + } + + /** + * {@inheritdoc} + */ + public function getRegion() + { + return $this->region; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheEntry.php new file mode 100644 index 00000000000..ece7fae18d1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheEntry.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Entity cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class EntityCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The entity map data + */ + public $data; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The entity class name + */ + public $class; + + /** + * @param string $class The entity class. + * @param array $data The entity data. + */ + public function __construct($class, array $data) + { + $this->class = $class; + $this->data = $data; + } + + /** + * Creates a new EntityCacheEntry + * + * This method allow Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + */ + public static function __set_state(array $values) + { + return new self($values['class'], $values['data']); + } + + /** + * Retrieves the entity data resolving cache entries + * + * @param \Doctrine\ORM\EntityManagerInterfac $em + * + * @return array + */ + public function resolveAssociationEntries(EntityManagerInterface $em) + { + return array_map(function($value) use ($em) { + if ( ! ($value instanceof AssociationCacheEntry)) { + return $value; + } + + return $em->getReference($value->class, $value->identifier); + }, $this->data); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php new file mode 100644 index 00000000000..281e610fa6c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines entity classes roles to be stored in the cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class EntityCacheKey extends CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The entity identifier + */ + public $identifier; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The entity class name + */ + public $entityClass; + + /** + * @param string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class. + * @param array $identifier The entity identifier + */ + public function __construct($entityClass, array $identifier) + { + ksort($identifier); + + $this->identifier = $identifier; + $this->entityClass = $entityClass; + $this->hash = str_replace('\\', '.', strtolower($entityClass) . '_' . implode(' ', $identifier)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityHydrator.php new file mode 100644 index 00000000000..05a394da7b2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityHydrator.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Hydrator cache entry for entities + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface EntityHydrator +{ + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key. + * @param object $entity The entity. + * + * @return \Doctrine\ORM\Cache\EntityCacheEntry + */ + public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity); + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key. + * @param \Doctrine\ORM\Cache\EntityCacheEntry $entry The entity cache entry. + * @param object $entity The entity to load the cache into. If not specified, a new entity is created. + */ + public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Lock.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Lock.php new file mode 100644 index 00000000000..5346aa32371 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Lock.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache Lock + * + * @since 2.5 + * @author Fabio B. Silva + */ +class Lock +{ + /** + * @var string + */ + public $value; + + /** + * @var integer + */ + public $time; + + /** + * @param string $value + * @param integer $time + */ + public function __construct($value, $time = null) + { + $this->value = $value; + $this->time = $time ? : time(); + } + + /** + * @return \Doctrine\ORM\Cache\Lock + */ + public static function createLockRead() + { + return new self(uniqid(time())); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/LockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/LockException.php new file mode 100644 index 00000000000..d4c76240aa9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/LockException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Lock exception for cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class LockException extends CacheException +{ + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLogger.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLogger.php new file mode 100644 index 00000000000..bdae4eea8c5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLogger.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Cache\Logging; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; + +/** + * Interface for logging. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CacheLogger +{ + /** + * Log an entity put into second level cache. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity. + */ + public function entityCachePut($regionName, EntityCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a hit. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity. + */ + public function entityCacheHit($regionName, EntityCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a miss. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity. + */ + public function entityCacheMiss($regionName, EntityCacheKey $key); + + /** + * Log an entity put into second level cache. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection. + */ + public function collectionCachePut($regionName, CollectionCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a hit. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection. + */ + public function collectionCacheHit($regionName, CollectionCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a miss. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection. + */ + public function collectionCacheMiss($regionName, CollectionCacheKey $key); + + /** + * Log a query put into the query cache. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query. + */ + public function queryCachePut($regionName, QueryCacheKey $key); + + /** + * Log a query get from the query cache resulted in a hit. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query. + */ + public function queryCacheHit($regionName, QueryCacheKey $key); + + /** + * Log a query get from the query cache resulted in a miss. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query. + */ + public function queryCacheMiss($regionName, QueryCacheKey $key); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php new file mode 100644 index 00000000000..694b35ca5eb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php @@ -0,0 +1,156 @@ +. + */ + +namespace Doctrine\ORM\Cache\Logging; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; + +/** + * Cache logger chain + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CacheLoggerChain implements CacheLogger +{ + /** + * @var array<\Doctrine\ORM\Cache\Logging\CacheLogger> + */ + private $loggers = array(); + + /** + * @param string $name + * @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger + */ + public function setLogger($name, CacheLogger $logger) + { + $this->loggers[$name] = $logger; + } + + /** + * @param string $name + * + * @return \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + public function getLogger($name) + { + return isset($this->loggers[$name]) ? $this->loggers[$name] : null; + } + + /** + * @return array<\Doctrine\ORM\Cache\Logging\CacheLogger> + */ + public function getLoggers() + { + return $this->loggers; + } + + /** + * {@inheritdoc} + */ + public function collectionCacheHit($regionName, CollectionCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->collectionCacheHit($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function collectionCacheMiss($regionName, CollectionCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->collectionCacheMiss($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function collectionCachePut($regionName, CollectionCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->collectionCachePut($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function entityCacheHit($regionName, EntityCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->entityCacheHit($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function entityCacheMiss($regionName, EntityCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->entityCacheMiss($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function entityCachePut($regionName, EntityCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->entityCachePut($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function queryCacheHit($regionName, QueryCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->queryCacheHit($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function queryCacheMiss($regionName, QueryCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->queryCacheMiss($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function queryCachePut($regionName, QueryCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->queryCachePut($regionName, $key); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/StatisticsCacheLogger.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/StatisticsCacheLogger.php new file mode 100644 index 00000000000..2fbba40bef1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/StatisticsCacheLogger.php @@ -0,0 +1,251 @@ +. + */ + +namespace Doctrine\ORM\Cache\Logging; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; + +/** + * Provide basic second level cache statistics. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class StatisticsCacheLogger implements CacheLogger +{ + /** + * @var array + */ + private $cacheMissCountMap = array(); + + /** + * @var array + */ + private $cacheHitCountMap = array(); + + /** + * @var array + */ + private $cachePutCountMap = array(); + + /** + * {@inheritdoc} + */ + public function collectionCacheMiss($regionName, CollectionCacheKey $key) + { + $this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName]) + ? $this->cacheMissCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function collectionCacheHit($regionName, CollectionCacheKey $key) + { + $this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName]) + ? $this->cacheHitCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function collectionCachePut($regionName, CollectionCacheKey $key) + { + $this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName]) + ? $this->cachePutCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function entityCacheMiss($regionName, EntityCacheKey $key) + { + $this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName]) + ? $this->cacheMissCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function entityCacheHit($regionName, EntityCacheKey $key) + { + $this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName]) + ? $this->cacheHitCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function entityCachePut($regionName, EntityCacheKey $key) + { + $this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName]) + ? $this->cachePutCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function queryCacheHit($regionName, QueryCacheKey $key) + { + $this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName]) + ? $this->cacheHitCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function queryCacheMiss($regionName, QueryCacheKey $key) + { + $this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName]) + ? $this->cacheMissCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function queryCachePut($regionName, QueryCacheKey $key) + { + $this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName]) + ? $this->cachePutCountMap[$regionName] + 1 + : 1; + } + + /** + * Get the number of entries successfully retrieved from cache. + * + * @param string $regionName The name of the cache region. + * + * @return integer + */ + public function getRegionHitCount($regionName) + { + return isset($this->cacheHitCountMap[$regionName]) ? $this->cacheHitCountMap[$regionName] : 0; + } + + /** + * Get the number of cached entries *not* found in cache. + * + * @param string $regionName The name of the cache region. + * + * @return integer + */ + public function getRegionMissCount($regionName) + { + return isset($this->cacheMissCountMap[$regionName]) ? $this->cacheMissCountMap[$regionName] : 0; + } + + /** + * Get the number of cacheable entries put in cache. + * + * @param string $regionName The name of the cache region. + * + * @return integer + */ + public function getRegionPutCount($regionName) + { + return isset($this->cachePutCountMap[$regionName]) ? $this->cachePutCountMap[$regionName] : 0; + } + + /** + * @return array + */ + public function getRegionsMiss() + { + return $this->cacheMissCountMap; + } + + /** + * @return array + */ + public function getRegionsHit() + { + return $this->cacheHitCountMap; + } + + /** + * @return array + */ + public function getRegionsPut() + { + return $this->cachePutCountMap; + } + + /** + * Clear region statistics + * + * @param string $regionName The name of the cache region. + */ + public function clearRegionStats($regionName) + { + $this->cachePutCountMap[$regionName] = 0; + $this->cacheHitCountMap[$regionName] = 0; + $this->cacheMissCountMap[$regionName] = 0; + } + + /** + * Clear all statistics + */ + public function clearStats() + { + $this->cachePutCountMap = array(); + $this->cacheHitCountMap = array(); + $this->cacheMissCountMap = array(); + } + + /** + * Get the total number of put in cache. + * + * @return integer + */ + public function getPutCount() + { + return array_sum($this->cachePutCountMap); + } + + /** + * Get the total number of entries successfully retrieved from cache. + * + * @return integer + */ + public function getHitCount() + { + return array_sum($this->cacheHitCountMap); + } + + /** + * Get the total number of cached entries *not* found in cache. + * + * @return integer + */ + public function getMissCount() + { + return array_sum($this->cacheMissCountMap); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/MultiGetRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/MultiGetRegion.php new file mode 100644 index 00000000000..be32b08f0fe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/MultiGetRegion.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines a region that supports multi-get reading. + * + * With one method call we can get multiple items. + * + * @since 2.5 + * @author Asmir Mustafic + */ +interface MultiGetRegion +{ + /** + * Get all items from the cache identified by $keys. + * It returns NULL if some elements can not be found. + * + * @param CollectionCacheEntry $collection The collection of the items to be retrieved. + * + * @return CacheEntry[]|null The cached entries or NULL if one or more entries can not be found + */ + public function getMultiple(CollectionCacheEntry $collection); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/CachedPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/CachedPersister.php new file mode 100644 index 00000000000..89afd32095f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/CachedPersister.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister; + +/** + * Interface for persister that support second level cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CachedPersister +{ + /** + * Perform whatever processing is encapsulated here after completion of the transaction. + */ + public function afterTransactionComplete(); + + /** + * Perform whatever processing is encapsulated here after completion of the rolled-back. + */ + public function afterTransactionRolledBack(); + + /** + * Gets the The region access. + * + * @return \Doctrine\ORM\Cache\Region + */ + public function getCacheRegion(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/AbstractCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/AbstractCollectionPersister.php new file mode 100644 index 00000000000..3172fe9994b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/AbstractCollectionPersister.php @@ -0,0 +1,304 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\Region; +use Doctrine\Common\Util\ClassUtils; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +abstract class AbstractCollectionPersister implements CachedCollectionPersister +{ + /** + * @var \Doctrine\ORM\UnitOfWork + */ + protected $uow; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + protected $metadataFactory; + + /** + * @var \Doctrine\ORM\Persisters\Collection\CollectionPersister + */ + protected $persister; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $sourceEntity; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $targetEntity; + + /** + * @var array + */ + protected $association; + + /** + * @var array + */ + protected $queuedCache = array(); + + /** + * @var \Doctrine\ORM\Cache\Region + */ + protected $region; + + /** + * @var string + */ + protected $regionName; + + /** + * @var \Doctrine\ORM\Cache\CollectionHydrator + */ + protected $hydrator; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger + */ + protected $cacheLogger; + + /** + * @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached. + * @param \Doctrine\ORM\Cache\Region $region The collection region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param array $association The association mapping. + */ + public function __construct(CollectionPersister $persister, Region $region, EntityManagerInterface $em, array $association) + { + $configuration = $em->getConfiguration(); + $cacheConfig = $configuration->getSecondLevelCacheConfiguration(); + $cacheFactory = $cacheConfig->getCacheFactory(); + + $this->region = $region; + $this->persister = $persister; + $this->association = $association; + $this->regionName = $region->getName(); + $this->uow = $em->getUnitOfWork(); + $this->metadataFactory = $em->getMetadataFactory(); + $this->cacheLogger = $cacheConfig->getCacheLogger(); + $this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association); + $this->sourceEntity = $em->getClassMetadata($association['sourceEntity']); + $this->targetEntity = $em->getClassMetadata($association['targetEntity']); + } + + /** + * {@inheritdoc} + */ + public function getCacheRegion() + { + return $this->region; + } + + /** + * {@inheritdoc} + */ + public function getSourceEntityMetadata() + { + return $this->sourceEntity; + } + + /** + * {@inheritdoc} + */ + public function getTargetEntityMetadata() + { + return $this->targetEntity; + } + + /** + * @param \Doctrine\ORM\PersistentCollection $collection + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key + * + * @return \Doctrine\ORM\PersistentCollection|null + */ + public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key) + { + if (($cache = $this->region->get($key)) === null) { + return null; + } + + if (($cache = $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection)) === null) { + return null; + } + + return $cache; + } + + /** + * {@inheritdoc} + */ + public function storeCollectionCache(CollectionCacheKey $key, $elements) + { + /* @var $targetPersister CachedEntityPersister */ + $targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName); + $targetRegion = $targetPersister->getCacheRegion(); + $targetHydrator = $targetPersister->getEntityHydrator(); + $entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements); + + foreach ($entry->identifiers as $index => $entityKey) { + if ($targetRegion->contains($entityKey)) { + continue; + } + + $class = $this->targetEntity; + $className = ClassUtils::getClass($elements[$index]); + + if ($className !== $this->targetEntity->name) { + $class = $this->metadataFactory->getMetadataFor($className); + } + + $entity = $elements[$index]; + $entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity); + + $targetRegion->put($entityKey, $entityEntry); + } + + $cached = $this->region->put($key, $entry); + + if ($this->cacheLogger && $cached) { + $this->cacheLogger->collectionCachePut($this->regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function contains(PersistentCollection $collection, $element) + { + return $this->persister->contains($collection, $element); + } + + /** + * {@inheritdoc} + */ + public function containsKey(PersistentCollection $collection, $key) + { + return $this->persister->containsKey($collection, $key); + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $collection) + { + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + $entry = $this->region->get($key); + + if ($entry !== null) { + return count($entry->identifiers); + } + + return $this->persister->count($collection); + } + + /** + * {@inheritdoc} + */ + public function get(PersistentCollection $collection, $index) + { + return $this->persister->get($collection, $index); + } + + /** + * {@inheritdoc} + */ + public function removeElement(PersistentCollection $collection, $element) + { + if ($persisterResult = $this->persister->removeElement($collection, $element)) { + $this->evictCollectionCache($collection); + $this->evictElementCache($this->sourceEntity->rootEntityName, $collection->getOwner()); + $this->evictElementCache($this->targetEntity->rootEntityName, $element); + } + + return $persisterResult; + } + + /** + * {@inheritdoc} + */ + public function slice(PersistentCollection $collection, $offset, $length = null) + { + return $this->persister->slice($collection, $offset, $length); + } + + /** + * {@inheritDoc} + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + { + return $this->persister->loadCriteria($collection, $criteria); + } + + /** + * Clears cache entries related to the current collection + * + * @param PersistentCollection $collection + */ + protected function evictCollectionCache(PersistentCollection $collection) + { + $key = new CollectionCacheKey( + $this->sourceEntity->rootEntityName, + $this->association['fieldName'], + $this->uow->getEntityIdentifier($collection->getOwner()) + ); + + $this->region->evict($key); + + if ($this->cacheLogger) { + $this->cacheLogger->collectionCachePut($this->regionName, $key); + } + } + + /** + * @param string $targetEntity + * @param object $element + */ + protected function evictElementCache($targetEntity, $element) + { + /* @var $targetPersister CachedEntityPersister */ + $targetPersister = $this->uow->getEntityPersister($targetEntity); + $targetRegion = $targetPersister->getCacheRegion(); + $key = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element)); + + $targetRegion->evict($key); + + if ($this->cacheLogger) { + $this->cacheLogger->entityCachePut($targetRegion->getName(), $key); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/CachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/CachedCollectionPersister.php new file mode 100644 index 00000000000..5722027c49d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/CachedCollectionPersister.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\PersistentCollection; + +/** + * Interface for second level cache collection persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface CachedCollectionPersister extends CachedPersister, CollectionPersister +{ + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getSourceEntityMetadata(); + + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getTargetEntityMetadata(); + + /** + * Loads a collection from cache + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key + * + * @return \Doctrine\ORM\PersistentCollection|null + */ + public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key); + + /** + * Stores a collection into cache + * + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key + * @param array|\Doctrine\Common\Collections\Collection $elements + * + * @return void + */ + public function storeCollectionCache(CollectionCacheKey $key, $elements); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php new file mode 100644 index 00000000000..86e5c8f9ffd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\PersistentCollection; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->storeCollectionCache($item['key'], $item['list']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $key) { + $this->region->evict($key); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + + $this->persister->delete($collection); + + $this->queuedCache['delete'][spl_object_hash($collection)] = $key; + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + $isInitialized = $collection->isInitialized(); + $isDirty = $collection->isDirty(); + + if ( ! $isInitialized && ! $isDirty) { + return; + } + + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + + // Invalidate non initialized collections OR ordered collection + if ($isDirty && ! $isInitialized || isset($this->association['orderBy'])) { + $this->persister->update($collection); + + $this->queuedCache['delete'][spl_object_hash($collection)] = $key; + + return; + } + + $this->persister->update($collection); + + $this->queuedCache['update'][spl_object_hash($collection)] = array( + 'key' => $key, + 'list' => $collection + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php new file mode 100644 index 00000000000..4f35ceb10dc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Cache\CacheException; +use Doctrine\Common\Util\ClassUtils; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + if ($collection->isDirty() && count($collection->getSnapshot()) > 0) { + throw CacheException::updateReadOnlyCollection(ClassUtils::getClass($collection->getOwner()), $this->association['fieldName']); + } + + parent::update($collection); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php new file mode 100644 index 00000000000..a92ed20d302 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\ConcurrentRegion; +use Doctrine\ORM\PersistentCollection; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister +{ + /** + * @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached. + * @param \Doctrine\ORM\Cache\ConcurrentRegion $region The collection region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param array $association The association mapping. + */ + public function __construct(CollectionPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, array $association) + { + parent::__construct($persister, $region, $em, $association); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + $lock = $this->region->lock($key); + + $this->persister->delete($collection); + + if ($lock === null) { + return; + } + + $this->queuedCache['delete'][spl_object_hash($collection)] = array( + 'key' => $key, + 'lock' => $lock + ); + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + $isInitialized = $collection->isInitialized(); + $isDirty = $collection->isDirty(); + + if ( ! $isInitialized && ! $isDirty) { + return; + } + + $this->persister->update($collection); + + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + $lock = $this->region->lock($key); + + if ($lock === null) { + return; + } + + $this->queuedCache['update'][spl_object_hash($collection)] = array( + 'key' => $key, + 'lock' => $lock + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php new file mode 100644 index 00000000000..eaead7cf750 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php @@ -0,0 +1,646 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Cache; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\TimestampCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Cache\CacheException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Collections\Criteria; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +abstract class AbstractEntityPersister implements CachedEntityPersister +{ + /** + * @var \Doctrine\ORM\UnitOfWork + */ + protected $uow; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + protected $metadataFactory; + + /** + * @var \Doctrine\ORM\Persisters\Entity\EntityPersister + */ + protected $persister; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $class; + + /** + * @var array + */ + protected $queuedCache = array(); + + /** + * @var \Doctrine\ORM\Cache\Region + */ + protected $region; + + /** + * @var \Doctrine\ORM\Cache\TimestampRegion + */ + protected $timestampRegion; + + /** + * @var \Doctrine\ORM\Cache\TimestampCacheKey + */ + protected $timestampKey; + + /** + * @var \Doctrine\ORM\Cache\EntityHydrator + */ + protected $hydrator; + + /** + * @var \Doctrine\ORM\Cache + */ + protected $cache; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger + */ + protected $cacheLogger; + + /** + * @var string + */ + protected $regionName; + + /** + * Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. + * + * @var array + */ + protected $joinedAssociations; + + /** + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache. + * @param \Doctrine\ORM\Cache\Region $region The entity cache region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Mapping\ClassMetadata $class The entity metadata. + */ + public function __construct(EntityPersister $persister, Region $region, EntityManagerInterface $em, ClassMetadata $class) + { + $configuration = $em->getConfiguration(); + $cacheConfig = $configuration->getSecondLevelCacheConfiguration(); + $cacheFactory = $cacheConfig->getCacheFactory(); + + $this->class = $class; + $this->region = $region; + $this->persister = $persister; + $this->cache = $em->getCache(); + $this->regionName = $region->getName(); + $this->uow = $em->getUnitOfWork(); + $this->metadataFactory = $em->getMetadataFactory(); + $this->cacheLogger = $cacheConfig->getCacheLogger(); + $this->timestampRegion = $cacheFactory->getTimestampRegion(); + $this->hydrator = $cacheFactory->buildEntityHydrator($em, $class); + $this->timestampKey = new TimestampCacheKey($this->class->getTableName()); + } + + /** + * {@inheritdoc} + */ + public function addInsert($entity) + { + $this->persister->addInsert($entity); + } + + /** + * {@inheritdoc} + */ + public function getInserts() + { + return $this->persister->getInserts(); + } + + /** + * {@inheritdoc} + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null) + { + return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy); + } + + /** + * {@inheritDoc} + */ + public function getCountSQL($criteria = array()) + { + return $this->persister->getCountSQL($criteria); + } + + /** + * {@inheritdoc} + */ + public function getInsertSQL() + { + return $this->persister->getInsertSQL(); + } + + /** + * {@inheritdoc} + */ + public function getResultSetMapping() + { + return $this->persister->getResultSetMapping(); + } + + /** + * {@inheritdoc} + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) + { + return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison); + } + + /** + * {@inheritdoc} + */ + public function exists($entity, Criteria $extraConditions = null) + { + if (null === $extraConditions) { + $key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity)); + + if ($this->region->contains($key)) { + return true; + } + } + + return $this->persister->exists($entity, $extraConditions); + } + + /** + * {@inheritdoc} + */ + public function getCacheRegion() + { + return $this->region; + } + + /** + * @return \Doctrine\ORM\Cache\EntityHydrator + */ + public function getEntityHydrator() + { + return $this->hydrator; + } + + /** + * {@inheritdoc} + */ + public function storeEntityCache($entity, EntityCacheKey $key) + { + $class = $this->class; + $className = ClassUtils::getClass($entity); + + if ($className !== $this->class->name) { + $class = $this->metadataFactory->getMetadataFor($className); + } + + if ($class->containsForeignIdentifier) { + foreach ($class->associationMappings as $name => $assoc) { + if (!empty($assoc['id']) && !isset($assoc['cache'])) { + throw CacheException::nonCacheableEntityAssociation($class->name, $name); + } + } + } + + $entry = $this->hydrator->buildCacheEntry($class, $key, $entity); + $cached = $this->region->put($key, $entry); + + if ($this->cacheLogger && $cached) { + $this->cacheLogger->entityCachePut($this->regionName, $key); + } + + return $cached; + } + + /** + * @param object $entity + */ + private function storeJoinedAssociations($entity) + { + if ($this->joinedAssociations === null) { + $associations = array(); + + foreach ($this->class->associationMappings as $name => $assoc) { + if (isset($assoc['cache']) && + ($assoc['type'] & ClassMetadata::TO_ONE) && + ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ! $assoc['isOwningSide'])) { + + $associations[] = $name; + } + } + + $this->joinedAssociations = $associations; + } + + foreach ($this->joinedAssociations as $name) { + $assoc = $this->class->associationMappings[$name]; + $assocEntity = $this->class->getFieldValue($entity, $name); + + if ($assocEntity === null) { + continue; + } + + $assocId = $this->uow->getEntityIdentifier($assocEntity); + $assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId); + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + + $assocPersister->storeEntityCache($assocEntity, $assocKey); + } + } + + /** + * Generates a string of currently query + * + * @param array $query + * @param string $criteria + * @param array $orderBy + * @param integer $limit + * @param integer $offset + * @param integer $timestamp + * + * @return string + */ + protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null, $timestamp = null) + { + list($params) = ($criteria instanceof Criteria) + ? $this->persister->expandCriteriaParameters($criteria) + : $this->persister->expandParameters($criteria); + + return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $timestamp); + } + + /** + * {@inheritdoc} + */ + public function expandParameters($criteria) + { + return $this->persister->expandParameters($criteria); + } + + /** + * {@inheritdoc} + */ + public function expandCriteriaParameters(Criteria $criteria) + { + return $this->persister->expandCriteriaParameters($criteria); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata() + { + return $this->persister->getClassMetadata(); + } + + /** + * {@inheritdoc} + */ + public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + return $this->persister->getManyToManyCollection($assoc, $sourceEntity, $offset, $limit); + } + + /** + * {@inheritdoc} + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit); + } + + /** + * {@inheritdoc} + */ + public function getOwningTable($fieldName) + { + return $this->persister->getOwningTable($fieldName); + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + $this->queuedCache['insert'] = $this->persister->getInserts(); + + return $this->persister->executeInserts(); + } + + /** + * {@inheritdoc} + */ + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = null, $limit = null, array $orderBy = null) + { + if ($entity !== null || $assoc !== null || ! empty($hints) || $lockMode !== null) { + return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy); + } + + //handle only EntityRepository#findOneBy + $timestamp = $this->timestampRegion->get($this->timestampKey); + $query = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy); + $hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null); + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL); + $queryCache = $this->cache->getQueryCache($this->regionName); + $result = $queryCache->get($querykey, $rsm); + + if ($result !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($this->regionName, $querykey); + } + + return $result[0]; + } + + if (($result = $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy)) === null) { + return null; + } + + $cached = $queryCache->put($querykey, $rsm, array($result)); + + if ($this->cacheLogger) { + if ($result) { + $this->cacheLogger->queryCacheMiss($this->regionName, $querykey); + } + + if ($cached) { + $this->cacheLogger->queryCachePut($this->regionName, $querykey); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $timestamp = $this->timestampRegion->get($this->timestampKey); + $query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); + $hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null); + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL); + $queryCache = $this->cache->getQueryCache($this->regionName); + $result = $queryCache->get($querykey, $rsm); + + if ($result !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($this->regionName, $querykey); + } + + return $result; + } + + $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset); + $cached = $queryCache->put($querykey, $rsm, $result); + + if ($this->cacheLogger) { + if ($result) { + $this->cacheLogger->queryCacheMiss($this->regionName, $querykey); + } + + if ($cached) { + $this->cacheLogger->queryCachePut($this->regionName, $querykey); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function loadById(array $identifier, $entity = null) + { + $cacheKey = new EntityCacheKey($this->class->rootEntityName, $identifier); + $cacheEntry = $this->region->get($cacheKey); + $class = $this->class; + + if ($cacheEntry !== null) { + if ($cacheEntry->class !== $this->class->name) { + $class = $this->metadataFactory->getMetadataFor($cacheEntry->class); + } + + if (($entity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity)) !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->entityCacheHit($this->regionName, $cacheKey); + } + + return $entity; + } + } + + $entity = $this->persister->loadById($identifier, $entity); + + if ($entity === null) { + return null; + } + + $class = $this->class; + $className = ClassUtils::getClass($entity); + + if ($className !== $this->class->name) { + $class = $this->metadataFactory->getMetadataFor($className); + } + + $cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity); + $cached = $this->region->put($cacheKey, $cacheEntry); + + if ($cached && ($this->joinedAssociations === null || count($this->joinedAssociations) > 0)) { + $this->storeJoinedAssociations($entity); + } + + if ($this->cacheLogger) { + if ($cached) { + $this->cacheLogger->entityCachePut($this->regionName, $cacheKey); + } + + $this->cacheLogger->entityCacheMiss($this->regionName, $cacheKey); + } + + return $entity; + } + + /** + * {@inheritDoc} + */ + public function count($criteria = array()) + { + return $this->persister->count($criteria); + } + + /** + * {@inheritdoc} + */ + public function loadCriteria(Criteria $criteria) + { + $query = $this->persister->getSelectSQL($criteria); + $timestamp = $this->timestampRegion->get($this->timestampKey); + $hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null); + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL); + $queryCache = $this->cache->getQueryCache($this->regionName); + $cacheResult = $queryCache->get($querykey, $rsm); + + if ($cacheResult !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($this->regionName, $querykey); + } + + return $cacheResult; + } + + $result = $this->persister->loadCriteria($criteria); + $cached = $queryCache->put($querykey, $rsm, $result); + + if ($this->cacheLogger) { + if ($result) { + $this->cacheLogger->queryCacheMiss($this->regionName, $querykey); + } + + if ($cached) { + $this->cacheLogger->queryCachePut($this->regionName, $querykey); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $persister = $this->uow->getCollectionPersister($assoc); + $hasCache = ($persister instanceof CachedPersister); + $key = null; + + if ($hasCache) { + $ownerId = $this->uow->getEntityIdentifier($coll->getOwner()); + $key = new CollectionCacheKey($assoc['sourceEntity'], $assoc['fieldName'], $ownerId); + $list = $persister->loadCollectionCache($coll, $key); + + if ($list !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key); + } + + return $list; + } + } + + $list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll); + + if ($hasCache) { + $persister->storeCollectionCache($key, $list); + + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); + } + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $persister = $this->uow->getCollectionPersister($assoc); + $hasCache = ($persister instanceof CachedPersister); + + if ($hasCache) { + $ownerId = $this->uow->getEntityIdentifier($coll->getOwner()); + $key = new CollectionCacheKey($assoc['sourceEntity'], $assoc['fieldName'], $ownerId); + $list = $persister->loadCollectionCache($coll, $key); + + if ($list !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key); + } + + return $list; + } + } + + $list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll); + + if ($hasCache) { + $persister->storeCollectionCache($key, $list); + + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); + } + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()) + { + return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier); + } + + /** + * {@inheritdoc} + */ + public function lock(array $criteria, $lockMode) + { + $this->persister->lock($criteria, $lockMode); + } + + /** + * {@inheritdoc} + */ + public function refresh(array $id, $entity, $lockMode = null) + { + $this->persister->refresh($id, $entity, $lockMode); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/CachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/CachedEntityPersister.php new file mode 100644 index 00000000000..0c9078cec72 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/CachedEntityPersister.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * Interface for second level cache entity persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface CachedEntityPersister extends CachedPersister, EntityPersister +{ + /** + * @return \Doctrine\ORM\Cache\EntityHydrator + */ + public function getEntityHydrator(); + + /** + * @param object $entity + * @param \Doctrine\ORM\Cache\EntityCacheKey $key + * @return boolean + */ + public function storeEntityCache($entity, EntityCacheKey $key); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php new file mode 100644 index 00000000000..d50d8c5009b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\Cache\EntityCacheKey; + +/** + * Specific non-strict read/write cached entity persister + * + * @author Fabio B. Silva + * @since 2.5 + */ +class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister +{ + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + $isChanged = false; + + if (isset($this->queuedCache['insert'])) { + foreach ($this->queuedCache['insert'] as $entity) { + $isChanged = $this->updateCache($entity, $isChanged); + } + } + + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $entity) { + $isChanged = $this->updateCache($entity, $isChanged); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $key) { + $this->region->evict($key); + + $isChanged = true; + } + } + + if ($isChanged) { + $this->timestampRegion->update($this->timestampKey); + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + + if ($this->persister->delete($entity)) { + $this->region->evict($key); + } + + $this->queuedCache['delete'][] = $key; + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $this->persister->update($entity); + + $this->queuedCache['update'][] = $entity; + } + + private function updateCache($entity, $isChanged) + { + $class = $this->metadataFactory->getMetadataFor(get_class($entity)); + $key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + $entry = $this->hydrator->buildCacheEntry($class, $key, $entity); + $cached = $this->region->put($key, $entry); + $isChanged = $isChanged ?: $cached; + + if ($this->cacheLogger && $cached) { + $this->cacheLogger->entityCachePut($this->regionName, $key); + } + + return $isChanged; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php new file mode 100644 index 00000000000..ce93d7b0e61 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Cache\CacheException; +use Doctrine\Common\Util\ClassUtils; + +/** + * Specific read-only region entity persister + * + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersister +{ + /** + * {@inheritdoc} + */ + public function update($entity) + { + throw CacheException::updateReadOnlyEntity(ClassUtils::getClass($entity)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php new file mode 100644 index 00000000000..8413082deac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php @@ -0,0 +1,139 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Persisters\Entity\EntityPersister; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\ConcurrentRegion; +use Doctrine\ORM\Cache\EntityCacheKey; + +/** + * Specific read-write entity persister + * + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadWriteCachedEntityPersister extends AbstractEntityPersister +{ + /** + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache. + * @param \Doctrine\ORM\Cache\ConcurrentRegion $region The entity cache region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Mapping\ClassMetadata $class The entity metadata. + */ + public function __construct(EntityPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, ClassMetadata $class) + { + parent::__construct($persister, $region, $em, $class); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + $isChanged = true; + + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + + $isChanged = true; + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + + $isChanged = true; + } + } + + if ($isChanged) { + $this->timestampRegion->update($this->timestampKey); + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + $lock = $this->region->lock($key); + + if ($this->persister->delete($entity)) { + $this->region->evict($key); + } + + if ($lock === null) { + return; + } + + $this->queuedCache['delete'][] = array( + 'lock' => $lock, + 'key' => $key + ); + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + $lock = $this->region->lock($key); + + $this->persister->update($entity); + + if ($lock === null) { + return; + } + + $this->queuedCache['update'][] = array( + 'lock' => $lock, + 'key' => $key + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCache.php new file mode 100644 index 00000000000..dd5ef3bf539 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCache.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * Defines the contract for caches capable of storing query results. + * These caches should only concern themselves with storing the matching result ids. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface QueryCache +{ + /** + * @return boolean + */ + public function clear(); + + /** + * @param \Doctrine\ORM\Cache\QueryCacheKey $key + * @param \Doctrine\ORM\Query\ResultSetMapping $rsm + * @param mixed $result + * @param array $hints + * + * @return boolean + */ + public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = array()); + + /** + * @param \Doctrine\ORM\Cache\QueryCacheKey $key + * @param \Doctrine\ORM\Query\ResultSetMapping $rsm + * @param array $hints + * + * @return array|null + */ + public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = array()); + + /** + * @return \Doctrine\ORM\Cache\Region + */ + public function getRegion(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheEntry.php new file mode 100644 index 00000000000..1ba61bcf090 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheEntry.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Query cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class QueryCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array List of entity identifiers + */ + public $result; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var integer Time creation of this cache entry + */ + public $time; + + /** + * @param array $result + * @param integer $time + */ + public function __construct($result, $time = null) + { + $this->result = $result; + $this->time = $time ?: time(); + } + + /** + * @param array $values + */ + public static function __set_state(array $values) + { + return new self($values['result'], $values['time']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheKey.php new file mode 100644 index 00000000000..9a7d2b7bcc8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheKey.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Cache; + +/** + * A cache key that identifies a particular query. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class QueryCacheKey extends CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var integer Cache key lifetime + */ + public $lifetime; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var integer Cache mode (Doctrine\ORM\Cache::MODE_*) + */ + public $cacheMode; + + /** + * @param string $hash Result cache id + * @param integer $lifetime Query lifetime + * @param integer $cacheMode Query cache mode + */ + public function __construct($hash, $lifetime = 0, $cacheMode = Cache::MODE_NORMAL) + { + $this->hash = $hash; + $this->lifetime = $lifetime; + $this->cacheMode = $cacheMode; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheValidator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheValidator.php new file mode 100644 index 00000000000..682a41ec4fa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheValidator.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache query validator interface. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface QueryCacheValidator +{ + /** + * Checks if the query entry is valid + * + * @param \Doctrine\ORM\Cache\QueryCacheKey $key + * @param \Doctrine\ORM\Cache\QueryCacheEntry $entry + * + * @return boolean + */ + public function isValid(QueryCacheKey $key, QueryCacheEntry $entry); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region.php new file mode 100644 index 00000000000..3bffbbce747 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines a contract for accessing a particular named region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface Region extends MultiGetRegion +{ + /** + * Retrieve the name of this region. + * + * @return string The region name + */ + public function getName(); + + /** + * Determine whether this region contains data for the given key. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The cache key + * + * @return boolean TRUE if the underlying cache contains corresponding data; FALSE otherwise. + */ + public function contains(CacheKey $key); + + /** + * Get an item from the cache. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to be retrieved. + * + * @return \Doctrine\ORM\Cache\CacheEntry|null The cached entry or NULL + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates a problem accessing the item or region. + */ + public function get(CacheKey $key); + + /** + * Put an item into the cache. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key under which to cache the item. + * @param \Doctrine\ORM\Cache\CacheEntry $entry The entry to cache. + * @param \Doctrine\ORM\Cache\Lock $lock The lock previously obtained. + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates a problem accessing the region. + */ + public function put(CacheKey $key, CacheEntry $entry, Lock $lock = null); + + /** + * Remove an item from the cache. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key under which to cache the item. + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates a problem accessing the region. + */ + public function evict(CacheKey $key); + + /** + * Remove all contents of this particular cache region. + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates problem accessing the region. + */ + public function evictAll(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php new file mode 100644 index 00000000000..7ecf7331116 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\Common\Cache\MultiGetCache; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\CollectionCacheEntry; + +/** + * A cache region that enables the retrieval of multiple elements with one call + * + * @since 2.5 + * @author Asmir Mustafic + */ +class DefaultMultiGetRegion extends DefaultRegion +{ + /** + * Note that the multiple type is due to doctrine/cache not integrating the MultiGetCache interface + * in its signature due to BC in 1.x + * + * @var MultiGetCache|\Doctrine\Common\Cache\Cache + */ + protected $cache; + + /** + * {@inheritDoc} + * + * @param MultiGetCache $cache + */ + public function __construct($name, MultiGetCache $cache, $lifetime = 0) + { + /* @var $cache \Doctrine\Common\Cache\Cache */ + parent::__construct($name, $cache, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function getMultiple(CollectionCacheEntry $collection) + { + $keysToRetrieve = array(); + + foreach ($collection->identifiers as $index => $key) { + $keysToRetrieve[$index] = $this->getCacheEntryKey($key); + } + + $items = $this->cache->fetchMultiple($keysToRetrieve); + if (count($items) !== count($keysToRetrieve)) { + return null; + } + + $returnableItems = array(); + foreach ($keysToRetrieve as $index => $key) { + $returnableItems[$index] = $items[$key]; + } + + return $returnableItems; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultRegion.php new file mode 100644 index 00000000000..3f214d0b0e9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultRegion.php @@ -0,0 +1,160 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\Common\Cache\Cache as CacheAdapter; +use Doctrine\Common\Cache\ClearableCache; +use Doctrine\ORM\Cache\CacheEntry; +use Doctrine\ORM\Cache\CacheKey; +use Doctrine\ORM\Cache\CollectionCacheEntry; +use Doctrine\ORM\Cache\Lock; +use Doctrine\ORM\Cache\Region; + +/** + * The simplest cache region compatible with all doctrine-cache drivers. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultRegion implements Region +{ + const REGION_KEY_SEPARATOR = '_'; + + /** + * @var CacheAdapter + */ + protected $cache; + + /** + * @var string + */ + protected $name; + + /** + * @var integer + */ + protected $lifetime = 0; + + /** + * @param string $name + * @param CacheAdapter $cache + * @param integer $lifetime + */ + public function __construct($name, CacheAdapter $cache, $lifetime = 0) + { + $this->cache = $cache; + $this->name = (string) $name; + $this->lifetime = (integer) $lifetime; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * @return \Doctrine\Common\Cache\CacheProvider + */ + public function getCache() + { + return $this->cache; + } + + /** + * {@inheritdoc} + */ + public function contains(CacheKey $key) + { + return $this->cache->contains($this->getCacheEntryKey($key)); + } + + /** + * {@inheritdoc} + */ + public function get(CacheKey $key) + { + return $this->cache->fetch($this->getCacheEntryKey($key)) ?: null; + } + + /** + * {@inheritdoc} + */ + public function getMultiple(CollectionCacheEntry $collection) + { + $result = array(); + + foreach ($collection->identifiers as $key) { + $entryKey = $this->getCacheEntryKey($key); + $entryValue = $this->cache->fetch($entryKey); + + if ($entryValue === false) { + return null; + } + + $result[] = $entryValue; + } + + return $result; + } + + /** + * @param CacheKey $key + * @return string + */ + protected function getCacheEntryKey(CacheKey $key) + { + return $this->name . self::REGION_KEY_SEPARATOR . $key->hash; + } + + /** + * {@inheritdoc} + */ + public function put(CacheKey $key, CacheEntry $entry, Lock $lock = null) + { + return $this->cache->save($this->getCacheEntryKey($key), $entry, $this->lifetime); + } + + /** + * {@inheritdoc} + */ + public function evict(CacheKey $key) + { + return $this->cache->delete($this->getCacheEntryKey($key)); + } + + /** + * {@inheritdoc} + */ + public function evictAll() + { + if (! $this->cache instanceof ClearableCache) { + throw new \BadMethodCallException(sprintf( + 'Clearing all cache entries is not supported by the supplied cache adapter of type %s', + get_class($this->cache) + )); + } + + return $this->cache->deleteAll(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/FileLockRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/FileLockRegion.php new file mode 100644 index 00000000000..d8d4b26948d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/FileLockRegion.php @@ -0,0 +1,265 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\ORM\Cache\CollectionCacheEntry; +use Doctrine\ORM\Cache\Lock; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\CacheKey; +use Doctrine\ORM\Cache\CacheEntry; +use Doctrine\ORM\Cache\ConcurrentRegion; + +/** + * Very naive concurrent region, based on file locks. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class FileLockRegion implements ConcurrentRegion +{ + const LOCK_EXTENSION = 'lock'; + + /** + * var \Doctrine\ORM\Cache\Region + */ + private $region; + + /** + * @var string + */ + private $directory; + + /** + * var integer + */ + private $lockLifetime; + + /** + * @param \Doctrine\ORM\Cache\Region $region + * @param string $directory + * @param string $lockLifetime + * + * @throws \InvalidArgumentException + */ + public function __construct(Region $region, $directory, $lockLifetime) + { + if ( ! is_dir($directory) && ! @mkdir($directory, 0775, true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $directory)); + } + + if ( ! is_writable($directory)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable.', $directory)); + } + + $this->region = $region; + $this->directory = $directory; + $this->lockLifetime = $lockLifetime; + } + + /** + * @param \Doctrine\ORM\Cache\CacheKey $key + * @param \Doctrine\ORM\Cache\Lock $lock + * + * @return boolean + */ + private function isLocked(CacheKey $key, Lock $lock = null) + { + $filename = $this->getLockFileName($key); + + if ( ! is_file($filename)) { + return false; + } + + $time = $this->getLockTime($filename); + $content = $this->getLockContent($filename); + + if ( ! $content || ! $time) { + @unlink($filename); + + return false; + } + + if ($lock && $content === $lock->value) { + return false; + } + + // outdated lock + if (($time + $this->lockLifetime) <= time()) { + @unlink($filename); + + return false; + } + + return true; + } + + /** + * @param \Doctrine\ORM\Cache\CacheKey $key + * + * return string + */ + private function getLockFileName(CacheKey $key) + { + return $this->directory . DIRECTORY_SEPARATOR . $key->hash . '.' . self::LOCK_EXTENSION; + } + + /** + * @param string $filename + * + * return string + */ + private function getLockContent($filename) + { + return @file_get_contents($filename); + } + + /** + * @param string $filename + * + * return integer + */ + private function getLockTime($filename) + { + return @fileatime($filename); + } + + /** + * {inheritdoc} + */ + public function getName() + { + return $this->region->getName(); + } + + /** + * {inheritdoc} + */ + public function contains(CacheKey $key) + { + if ($this->isLocked($key)) { + return false; + } + + return $this->region->contains($key); + } + + /** + * {inheritdoc} + */ + public function get(CacheKey $key) + { + if ($this->isLocked($key)) { + return null; + } + + return $this->region->get($key); + } + + /** + * {@inheritdoc} + */ + public function getMultiple(CollectionCacheEntry $collection) + { + if (array_filter(array_map([$this, 'isLocked'], $collection->identifiers))) { + return null; + } + + return $this->region->getMultiple($collection); + } + + /** + * {inheritdoc} + */ + public function put(CacheKey $key, CacheEntry $entry, Lock $lock = null) + { + if ($this->isLocked($key, $lock)) { + return false; + } + + return $this->region->put($key, $entry); + } + + /** + * {inheritdoc} + */ + public function evict(CacheKey $key) + { + if ($this->isLocked($key)) { + @unlink($this->getLockFileName($key)); + } + + return $this->region->evict($key); + } + + /** + * {inheritdoc} + */ + public function evictAll() + { + // The check below is necessary because on some platforms glob returns false + // when nothing matched (even though no errors occurred) + $filenames = glob(sprintf("%s/*.%s" , $this->directory, self::LOCK_EXTENSION)); + + if ($filenames) { + foreach ($filenames as $filename) { + @unlink($filename); + } + } + + return $this->region->evictAll(); + } + + /** + * {inheritdoc} + */ + public function lock(CacheKey $key) + { + if ($this->isLocked($key)) { + return null; + } + + $lock = Lock::createLockRead(); + $filename = $this->getLockFileName($key); + + if ( ! @file_put_contents($filename, $lock->value, LOCK_EX)) { + return null; + } + chmod($filename, 0664); + + return $lock; + } + + /** + * {inheritdoc} + */ + public function unlock(CacheKey $key, Lock $lock) + { + if ($this->isLocked($key, $lock)) { + return false; + } + + if ( ! @unlink($this->getLockFileName($key))) { + return false; + } + + return true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php new file mode 100644 index 00000000000..dfdf9062aa3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\ORM\Cache\TimestampCacheEntry; +use Doctrine\ORM\Cache\TimestampRegion; +use Doctrine\ORM\Cache\CacheKey; + +/** + * Tracks the timestamps of the most recent updates to particular keys. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class UpdateTimestampCache extends DefaultRegion implements TimestampRegion +{ + /** + * {@inheritdoc} + */ + public function update(CacheKey $key) + { + $this->put($key, new TimestampCacheEntry); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/RegionsConfiguration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/RegionsConfiguration.php new file mode 100644 index 00000000000..0d060636cf1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/RegionsConfiguration.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache regions configuration + * + * @since 2.5 + * @author Fabio B. Silva + */ +class RegionsConfiguration +{ + /** + * @var array + */ + private $lifetimes = array(); + + /** + * @var array + */ + private $lockLifetimes = array(); + + /** + * @var integer + */ + private $defaultLifetime; + + /** + * @var integer + */ + private $defaultLockLifetime; + + /** + * @param integer $defaultLifetime + * @param integer $defaultLockLifetime + */ + public function __construct($defaultLifetime = 3600, $defaultLockLifetime = 60) + { + $this->defaultLifetime = (integer) $defaultLifetime; + $this->defaultLockLifetime = (integer) $defaultLockLifetime; + } + + /** + * @return integer + */ + public function getDefaultLifetime() + { + return $this->defaultLifetime; + } + + /** + * @param integer $defaultLifetime + */ + public function setDefaultLifetime($defaultLifetime) + { + $this->defaultLifetime = (integer) $defaultLifetime; + } + + /** + * @return integer + */ + public function getDefaultLockLifetime() + { + return $this->defaultLockLifetime; + } + + /** + * @param integer $defaultLockLifetime + */ + public function setDefaultLockLifetime($defaultLockLifetime) + { + $this->defaultLockLifetime = (integer) $defaultLockLifetime; + } + + /** + * @param string $regionName + * + * @return integer + */ + public function getLifetime($regionName) + { + return isset($this->lifetimes[$regionName]) + ? $this->lifetimes[$regionName] + : $this->defaultLifetime; + } + + /** + * @param string $name + * @param integer $lifetime + */ + public function setLifetime($name, $lifetime) + { + $this->lifetimes[$name] = (integer) $lifetime; + } + + /** + * @param string $regionName + * + * @return integer + */ + public function getLockLifetime($regionName) + { + return isset($this->lockLifetimes[$regionName]) + ? $this->lockLifetimes[$regionName] + : $this->defaultLockLifetime; + } + + /** + * @param string $name + * @param integer $lifetime + */ + public function setLockLifetime($name, $lifetime) + { + $this->lockLifetimes[$name] = (integer) $lifetime; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheEntry.php new file mode 100644 index 00000000000..9d15c84b384 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheEntry.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Timestamp cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class TimestampCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var float + */ + public $time; + + /** + * @param float $time + */ + public function __construct($time = null) + { + $this->time = $time ? (float)$time : microtime(true); + } + + /** + * Creates a new TimestampCacheEntry + * + * This method allow Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + */ + public static function __set_state(array $values) + { + return new self($values['time']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheKey.php new file mode 100644 index 00000000000..dfa72274b23 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheKey.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * A key that identifies a timestamped space. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class TimestampCacheKey extends CacheKey +{ + /** + * @param string $space Result cache id + */ + public function __construct($space) + { + $this->hash = (string) $space; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php new file mode 100644 index 00000000000..4e504e4f8da --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * @since 2.5 + * @author Fabio B. Silva + */ +class TimestampQueryCacheValidator implements QueryCacheValidator +{ + /** + * {@inheritdoc} + */ + public function isValid(QueryCacheKey $key, QueryCacheEntry $entry) + { + if ($key->lifetime == 0) { + return true; + } + + return ($entry->time + $key->lifetime) > time(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampRegion.php new file mode 100644 index 00000000000..9e0c25ca6ab --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampRegion.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines the contract for a cache region which will specifically be used to store entity "update timestamps". + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface TimestampRegion extends Region +{ + /** + * Update an specific key into the cache region. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to update the timestamp. + * + * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. + */ + public function update(CacheKey $key); +} 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 00000000000..c99c404751b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php @@ -0,0 +1,927 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Doctrine\Common\Annotations\CachedReader; +use Doctrine\Common\Annotations\SimpleAnnotationReader; +use Doctrine\Common\Cache\ArrayCache; +use Doctrine\Common\Cache\Cache as CacheDriver; +use Doctrine\Common\Proxy\AbstractProxyFactory; +use Doctrine\ORM\Cache\CacheConfiguration; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; +use Doctrine\ORM\Mapping\DefaultNamingStrategy; +use Doctrine\ORM\Mapping\DefaultQuoteStrategy; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\EntityListenerResolver; +use Doctrine\ORM\Mapping\NamingStrategy; +use Doctrine\ORM\Mapping\QuoteStrategy; +use Doctrine\ORM\Repository\DefaultRepositoryFactory; +use Doctrine\ORM\Repository\RepositoryFactory; + +/** + * Configuration container for all configuration options of Doctrine. + * It combines all configuration options from DBAL & ORM. + * + * Internal note: When adding a new configuration option just write a getter/setter pair. + * + * @since 2.0 + * @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 + * + * @return void + */ + public function setProxyDir($dir) + { + $this->_attributes['proxyDir'] = $dir; + } + + /** + * Gets the directory where Doctrine generates any necessary proxy class files. + * + * @return string|null + */ + public function getProxyDir() + { + return isset($this->_attributes['proxyDir']) + ? $this->_attributes['proxyDir'] + : null; + } + + /** + * Gets the strategy for automatically generating proxy classes. + * + * @return int Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory. + */ + public function getAutoGenerateProxyClasses() + { + return isset($this->_attributes['autoGenerateProxyClasses']) + ? $this->_attributes['autoGenerateProxyClasses'] + : AbstractProxyFactory::AUTOGENERATE_ALWAYS; + } + + /** + * Sets the strategy for automatically generating proxy classes. + * + * @param boolean|int $autoGenerate Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory. + * True is converted to AUTOGENERATE_ALWAYS, false to AUTOGENERATE_NEVER. + * + * @return void + */ + public function setAutoGenerateProxyClasses($autoGenerate) + { + $this->_attributes['autoGenerateProxyClasses'] = (int)$autoGenerate; + } + + /** + * Gets the namespace where proxy classes reside. + * + * @return string|null + */ + public function getProxyNamespace() + { + return isset($this->_attributes['proxyNamespace']) + ? $this->_attributes['proxyNamespace'] + : null; + } + + /** + * Sets the namespace where proxy classes reside. + * + * @param string $ns + * + * @return void + */ + public function setProxyNamespace($ns) + { + $this->_attributes['proxyNamespace'] = $ns; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param MappingDriver $driverImpl + * + * @return void + * + * @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(MappingDriver $driverImpl) + { + $this->_attributes['metadataDriverImpl'] = $driverImpl; + } + + /** + * Adds a new default annotation driver with a correctly configured annotation reader. If $useSimpleAnnotationReader + * is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported. + * + * @param array $paths + * @param bool $useSimpleAnnotationReader + * + * @return AnnotationDriver + */ + public function newDefaultAnnotationDriver($paths = array(), $useSimpleAnnotationReader = true) + { + AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php'); + + if ($useSimpleAnnotationReader) { + // Register the ORM Annotations in the AnnotationRegistry + $reader = new SimpleAnnotationReader(); + $reader->addNamespace('Doctrine\ORM\Mapping'); + $cachedReader = new CachedReader($reader, new ArrayCache()); + + return new AnnotationDriver($cachedReader, (array) $paths); + } + + return new AnnotationDriver( + new CachedReader(new AnnotationReader(), new ArrayCache()), + (array) $paths + ); + } + + /** + * Adds a namespace under a certain alias. + * + * @param string $alias + * @param string $namespace + * + * @return void + */ + 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 ORMException + */ + public function getEntityNamespace($entityNamespaceAlias) + { + if ( ! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) { + throw ORMException::unknownEntityNamespace($entityNamespaceAlias); + } + + return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\'); + } + + /** + * Sets the entity alias map. + * + * @param array $entityNamespaces + * + * @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. + * + * @return MappingDriver|null + * + * @throws ORMException + */ + 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|null + */ + 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 + * + * @return void + */ + public function setQueryCacheImpl(CacheDriver $cacheImpl) + { + $this->_attributes['queryCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache|null + */ + 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 + * + * @return void + */ + public function setHydrationCacheImpl(CacheDriver $cacheImpl) + { + $this->_attributes['hydrationCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for metadata caching. + * + * @return \Doctrine\Common\Cache\Cache|null + */ + 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 + * + * @return void + */ + public function setMetadataCacheImpl(CacheDriver $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. + * + * @return void + */ + 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. + * + * @throws ORMException + */ + 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 Query\ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query. + * + * @return void + */ + 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. + * + * @throws ORMException + */ + 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. + * + * @return void + * + * @throws ORMException If a configuration setting has a value that is not + * suitable for a production environment. + */ + public function ensureProductionSettings() + { + $queryCacheImpl = $this->getQueryCacheImpl(); + + if ( ! $queryCacheImpl) { + throw ORMException::queryCacheNotConfigured(); + } + + if ($queryCacheImpl instanceof ArrayCache) { + throw ORMException::queryCacheUsesNonPersistentCache($queryCacheImpl); + } + + $metadataCacheImpl = $this->getMetadataCacheImpl(); + + if ( ! $metadataCacheImpl) { + throw ORMException::metadataCacheNotConfigured(); + } + + if ($metadataCacheImpl instanceof ArrayCache) { + throw ORMException::metadataCacheUsesNonPersistentCache($metadataCacheImpl); + } + + 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 Function name. + * @param string|callable $className Class name or a callable that returns the function. + * + * @return void + * + * @throws ORMException + */ + public function addCustomStringFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customStringFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom string DQL function. + * + * @param string $name + * + * @return string|null + */ + 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. + * + * @return void + */ + public function setCustomStringFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomStringFunction($name, $className); + } + } + + /** + * 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 Function name. + * @param string|callable $className Class name or a callable that returns the function. + * + * @return void + * + * @throws ORMException + */ + public function addCustomNumericFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customNumericFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom numeric DQL function. + * + * @param string $name + * + * @return string|null + */ + 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. + * + * @return void + */ + public function setCustomNumericFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomNumericFunction($name, $className); + } + } + + /** + * 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 Function name. + * @param string|callable $className Class name or a callable that returns the function. + * + * @return void + * + * @throws ORMException + */ + public function addCustomDatetimeFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom date/time DQL function. + * + * @param string $name + * + * @return string|null + */ + 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. + * + * @return void + */ + public function setCustomDatetimeFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomDatetimeFunction($name, $className); + } + } + + /** + * Sets the custom hydrator modes in one pass. + * + * @param array $modes An array of ($modeName => $hydrator). + * + * @return void + */ + public function setCustomHydrationModes($modes) + { + $this->_attributes['customHydrationModes'] = array(); + + foreach ($modes as $modeName => $hydrator) { + $this->addCustomHydrationMode($modeName, $hydrator); + } + } + + /** + * Gets the hydrator class for the given hydration mode name. + * + * @param string $modeName The hydration mode name. + * + * @return string|null The hydrator class name. + */ + public function getCustomHydrationMode($modeName) + { + return isset($this->_attributes['customHydrationModes'][$modeName]) + ? $this->_attributes['customHydrationModes'][$modeName] + : null; + } + + /** + * Adds a custom hydration mode. + * + * @param string $modeName The hydration mode name. + * @param string $hydrator The hydrator class name. + * + * @return void + */ + public function addCustomHydrationMode($modeName, $hydrator) + { + $this->_attributes['customHydrationModes'][$modeName] = $hydrator; + } + + /** + * Sets a class metadata factory. + * + * @param string $cmfName + * + * @return void + */ + 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']; + } + + /** + * Adds 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; + } + + /** + * Sets default repository class. + * + * @since 2.2 + * + * @param string $className + * + * @return void + * + * @throws ORMException If not is a \Doctrine\Common\Persistence\ObjectRepository + */ + public function setDefaultRepositoryClassName($className) + { + $reflectionClass = new \ReflectionClass($className); + + if ( ! $reflectionClass->implementsInterface('Doctrine\Common\Persistence\ObjectRepository')) { + 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'; + } + + /** + * Sets naming strategy. + * + * @since 2.3 + * + * @param NamingStrategy $namingStrategy + * + * @return void + */ + public function setNamingStrategy(NamingStrategy $namingStrategy) + { + $this->_attributes['namingStrategy'] = $namingStrategy; + } + + /** + * Gets naming strategy.. + * + * @since 2.3 + * + * @return NamingStrategy + */ + public function getNamingStrategy() + { + if ( ! isset($this->_attributes['namingStrategy'])) { + $this->_attributes['namingStrategy'] = new DefaultNamingStrategy(); + } + + return $this->_attributes['namingStrategy']; + } + + /** + * Sets quote strategy. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\QuoteStrategy $quoteStrategy + * + * @return void + */ + public function setQuoteStrategy(QuoteStrategy $quoteStrategy) + { + $this->_attributes['quoteStrategy'] = $quoteStrategy; + } + + /** + * Gets quote strategy. + * + * @since 2.3 + * + * @return \Doctrine\ORM\Mapping\QuoteStrategy + */ + public function getQuoteStrategy() + { + if ( ! isset($this->_attributes['quoteStrategy'])) { + $this->_attributes['quoteStrategy'] = new DefaultQuoteStrategy(); + } + + return $this->_attributes['quoteStrategy']; + } + + /** + * Set the entity listener resolver. + * + * @since 2.4 + * @param \Doctrine\ORM\Mapping\EntityListenerResolver $resolver + */ + public function setEntityListenerResolver(EntityListenerResolver $resolver) + { + $this->_attributes['entityListenerResolver'] = $resolver; + } + + /** + * Get the entity listener resolver. + * + * @since 2.4 + * @return \Doctrine\ORM\Mapping\EntityListenerResolver + */ + public function getEntityListenerResolver() + { + if ( ! isset($this->_attributes['entityListenerResolver'])) { + $this->_attributes['entityListenerResolver'] = new DefaultEntityListenerResolver(); + } + + return $this->_attributes['entityListenerResolver']; + } + + /** + * Set the entity repository factory. + * + * @since 2.4 + * @param \Doctrine\ORM\Repository\RepositoryFactory $repositoryFactory + */ + public function setRepositoryFactory(RepositoryFactory $repositoryFactory) + { + $this->_attributes['repositoryFactory'] = $repositoryFactory; + } + + /** + * Get the entity repository factory. + * + * @since 2.4 + * @return \Doctrine\ORM\Repository\RepositoryFactory + */ + public function getRepositoryFactory() + { + return isset($this->_attributes['repositoryFactory']) + ? $this->_attributes['repositoryFactory'] + : new DefaultRepositoryFactory(); + } + + /** + * @since 2.5 + * + * @return boolean + */ + public function isSecondLevelCacheEnabled() + { + return isset($this->_attributes['isSecondLevelCacheEnabled']) + ? $this->_attributes['isSecondLevelCacheEnabled'] + : false; + } + + /** + * @since 2.5 + * + * @param boolean $flag + * + * @return void + */ + public function setSecondLevelCacheEnabled($flag = true) + { + $this->_attributes['isSecondLevelCacheEnabled'] = (boolean) $flag; + } + + /** + * @since 2.5 + * + * @param \Doctrine\ORM\Cache\CacheConfiguration $cacheConfig + * + * @return void + */ + public function setSecondLevelCacheConfiguration(CacheConfiguration $cacheConfig) + { + $this->_attributes['secondLevelCacheConfiguration'] = $cacheConfig; + } + + /** + * @since 2.5 + * + * @return \Doctrine\ORM\Cache\CacheConfiguration|null + */ + public function getSecondLevelCacheConfiguration() + { + if ( ! isset($this->_attributes['secondLevelCacheConfiguration']) && $this->isSecondLevelCacheEnabled()) { + $this->_attributes['secondLevelCacheConfiguration'] = new CacheConfiguration(); + } + + return isset($this->_attributes['secondLevelCacheConfiguration']) + ? $this->_attributes['secondLevelCacheConfiguration'] + : null; + } + + /** + * Returns query hints, which will be applied to every query in application + * + * @since 2.5 + * + * @return array + */ + public function getDefaultQueryHints() + { + return isset($this->_attributes['defaultQueryHints']) ? $this->_attributes['defaultQueryHints'] : array(); + } + + /** + * Sets array of query hints, which will be applied to every query in application + * + * @since 2.5 + * + * @param array $defaultQueryHints + */ + public function setDefaultQueryHints(array $defaultQueryHints) + { + $this->_attributes['defaultQueryHints'] = $defaultQueryHints; + } + + /** + * Gets the value of a default query hint. If the hint name is not recognized, FALSE is returned. + * + * @since 2.5 + * + * @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 getDefaultQueryHint($name) + { + return isset($this->_attributes['defaultQueryHints'][$name]) + ? $this->_attributes['defaultQueryHints'][$name] + : false; + } + + /** + * Sets a default query hint. If the hint name is not recognized, it is silently ignored. + * + * @since 2.5 + * + * @param string $name The name of the hint. + * @param mixed $value The value of the hint. + */ + public function setDefaultQueryHint($name, $value) + { + $this->_attributes['defaultQueryHints'][$name] = $value; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php new file mode 100644 index 00000000000..69cc6f50739 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php @@ -0,0 +1,278 @@ +. + */ + +namespace Doctrine\ORM\Decorator; + +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Common\Persistence\ObjectManagerDecorator; + +/** + * Base class for EntityManager decorators + * + * @since 2.4 + * @author Lars Strojny wrapped = $wrapped; + } + + /** + * {@inheritdoc} + */ + public function getConnection() + { + return $this->wrapped->getConnection(); + } + + /** + * {@inheritdoc} + */ + public function getExpressionBuilder() + { + return $this->wrapped->getExpressionBuilder(); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + return $this->wrapped->beginTransaction(); + } + + /** + * {@inheritdoc} + */ + public function transactional($func) + { + return $this->wrapped->transactional($func); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->wrapped->commit(); + } + + /** + * {@inheritdoc} + */ + public function rollback() + { + return $this->wrapped->rollback(); + } + + /** + * {@inheritdoc} + */ + public function createQuery($dql = '') + { + return $this->wrapped->createQuery($dql); + } + + /** + * {@inheritdoc} + */ + public function createNamedQuery($name) + { + return $this->wrapped->createNamedQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + return $this->wrapped->createNativeQuery($sql, $rsm); + } + + /** + * {@inheritdoc} + */ + public function createNamedNativeQuery($name) + { + return $this->wrapped->createNamedNativeQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createQueryBuilder() + { + return $this->wrapped->createQueryBuilder(); + } + + /** + * {@inheritdoc} + */ + public function getReference($entityName, $id) + { + return $this->wrapped->getReference($entityName, $id); + } + + /** + * {@inheritdoc} + */ + public function getPartialReference($entityName, $identifier) + { + return $this->wrapped->getPartialReference($entityName, $identifier); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->wrapped->close(); + } + + /** + * {@inheritdoc} + */ + public function copy($entity, $deep = false) + { + return $this->wrapped->copy($entity, $deep); + } + + /** + * {@inheritdoc} + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + return $this->wrapped->lock($entity, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function find($entityName, $id, $lockMode = null, $lockVersion = null) + { + return $this->wrapped->find($entityName, $id, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function flush($entity = null) + { + return $this->wrapped->flush($entity); + } + + /** + * {@inheritdoc} + */ + public function getEventManager() + { + return $this->wrapped->getEventManager(); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() + { + return $this->wrapped->getConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function isOpen() + { + return $this->wrapped->isOpen(); + } + + /** + * {@inheritdoc} + */ + public function getUnitOfWork() + { + return $this->wrapped->getUnitOfWork(); + } + + /** + * {@inheritdoc} + */ + public function getHydrator($hydrationMode) + { + return $this->wrapped->getHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function newHydrator($hydrationMode) + { + return $this->wrapped->newHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function getProxyFactory() + { + return $this->wrapped->getProxyFactory(); + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return $this->wrapped->getFilters(); + } + + /** + * {@inheritdoc} + */ + public function isFiltersStateClean() + { + return $this->wrapped->isFiltersStateClean(); + } + + /** + * {@inheritdoc} + */ + public function hasFilters() + { + return $this->wrapped->hasFilters(); + } + + /** + * {@inheritdoc} + */ + public function getCache() + { + return $this->wrapped->getCache(); + } +} 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 00000000000..685fc0d71d7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php @@ -0,0 +1,880 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Proxy\ProxyFactory; +use Doctrine\ORM\Query\FilterCollection; +use Doctrine\Common\Util\ClassUtils; + +/** + * The EntityManager is the central access point to ORM functionality. + * + * It is a facade to all different ORM subsystems such as UnitOfWork, + * Query Language and Repository API. Instantiation is done through + * the static create() method. The quickest way to obtain a fully + * configured EntityManager is: + * + * use Doctrine\ORM\Tools\Setup; + * use Doctrine\ORM\EntityManager; + * + * $paths = array('/path/to/entity/mapping/files'); + * + * $config = Setup::createAnnotationMetadataConfiguration($paths); + * $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true); + * $entityManager = EntityManager::create($dbParams, $config); + * + * For more information see + * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html} + * + * You should never attempt to inherit from the EntityManager: Inheritance + * is not a valid extension point for the EntityManager. Instead you + * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator} + * and wrap your entity manager in a decorator. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +/* final */class EntityManager implements EntityManagerInterface +{ + /** + * 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 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 proxy factory used to create dynamic proxies. + * + * @var \Doctrine\ORM\Proxy\ProxyFactory + */ + private $proxyFactory; + + /** + * The repository factory used to create dynamic repositories. + * + * @var \Doctrine\ORM\Repository\RepositoryFactory + */ + private $repositoryFactory; + + /** + * 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; + + /** + * @var \Doctrine\ORM\Cache The second level cache regions API. + */ + private $cache; + + /** + * 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->repositoryFactory = $config->getRepositoryFactory(); + $this->unitOfWork = new UnitOfWork($this); + $this->proxyFactory = new ProxyFactory( + $this, + $config->getProxyDir(), + $config->getProxyNamespace(), + $config->getAutoGenerateProxyClasses() + ); + + if ($config->isSecondLevelCacheEnabled()) { + $cacheConfig = $config->getSecondLevelCacheConfiguration(); + $cacheFactory = $cacheConfig->getCacheFactory(); + $this->cache = $cacheFactory->createCache($this); + } + } + + /** + * {@inheritDoc} + */ + 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; + } + + /** + * {@inheritDoc} + */ + public function getExpressionBuilder() + { + if ($this->expressionBuilder === null) { + $this->expressionBuilder = new Query\Expr; + } + + return $this->expressionBuilder; + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->conn->beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function getCache() + { + return $this->cache; + } + + /** + * {@inheritDoc} + */ + public function transactional($func) + { + if (!is_callable($func)) { + throw new \InvalidArgumentException('Expected argument of type "callable", got "' . gettype($func) . '"'); + } + + $this->conn->beginTransaction(); + + try { + $return = call_user_func($func, $this); + + $this->flush(); + $this->conn->commit(); + + return $return ?: true; + } catch (Exception $e) { + $this->close(); + $this->conn->rollback(); + + throw $e; + } + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->conn->commit(); + } + + /** + * {@inheritDoc} + */ + 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 + * + * Internal note: Performance-sensitive method. + * + * @param string $className + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getClassMetadata($className) + { + return $this->metadataFactory->getMetadataFor($className); + } + + /** + * {@inheritDoc} + */ + public function createQuery($dql = '') + { + $query = new Query($this); + + if ( ! empty($dql)) { + $query->setDql($dql); + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function createNamedQuery($name) + { + return $this->createQuery($this->config->getNamedQuery($name)); + } + + /** + * {@inheritDoc} + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + $query = new NativeQuery($this); + + $query->setSql($sql); + $query->setResultSetMapping($rsm); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function createNamedNativeQuery($name) + { + list($sql, $rsm) = $this->config->getNamedNativeQuery($name); + + return $this->createNativeQuery($sql, $rsm); + } + + /** + * {@inheritDoc} + */ + 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 null|object|array $entity + * + * @return void + * + * @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. + * + * @param string $entityName The class name of the entity to find. + * @param mixed $id The identity of the entity to find. + * @param integer|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * during the search. + * @param integer|null $lockVersion The version of the entity to find when using + * optimistic locking. + * + * @return object|null The entity instance or NULL if the entity can not be found. + * + * @throws OptimisticLockException + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws ORMException + */ + public function find($entityName, $id, $lockMode = null, $lockVersion = null) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + if ( ! is_array($id)) { + if ($class->isIdentifierComposite) { + throw ORMInvalidArgumentException::invalidCompositeIdentifier(); + } + + $id = array($class->identifier[0] => $id); + } + + foreach ($id as $i => $value) { + if (is_object($value) && $this->metadataFactory->hasMetadataFor(ClassUtils::getClass($value))) { + $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value); + + if ($id[$i] === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + } + + $sortedId = array(); + + foreach ($class->identifier as $identifier) { + if ( ! isset($id[$identifier])) { + throw ORMException::missingIdentifierField($class->name, $identifier); + } + + $sortedId[$identifier] = $id[$identifier]; + unset($id[$identifier]); + } + + if ($id) { + throw ORMException::unrecognizedIdentifierFields($class->name, array_keys($id)); + } + + $unitOfWork = $this->getUnitOfWork(); + + // Check identity map first + if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) { + if ( ! ($entity instanceof $class->name)) { + return null; + } + + switch (true) { + case LockMode::OPTIMISTIC === $lockMode: + $this->lock($entity, $lockMode, $lockVersion); + break; + + case LockMode::NONE === $lockMode: + case LockMode::PESSIMISTIC_READ === $lockMode: + case LockMode::PESSIMISTIC_WRITE === $lockMode: + $persister = $unitOfWork->getEntityPersister($class->name); + $persister->refresh($sortedId, $entity, $lockMode); + break; + } + + return $entity; // Hit! + } + + $persister = $unitOfWork->getEntityPersister($class->name); + + switch (true) { + case LockMode::OPTIMISTIC === $lockMode: + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($class->name); + } + + $entity = $persister->load($sortedId); + + $unitOfWork->lock($entity, $lockMode, $lockVersion); + + return $entity; + + case LockMode::NONE === $lockMode: + case LockMode::PESSIMISTIC_READ === $lockMode: + case LockMode::PESSIMISTIC_WRITE === $lockMode: + if ( ! $this->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + return $persister->load($sortedId, null, null, array(), $lockMode); + + default: + return $persister->loadById($sortedId); + } + } + + /** + * {@inheritDoc} + */ + 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)) !== false) { + 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; + } + + /** + * {@inheritDoc} + */ + 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)) !== false) { + 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|null $entityName if given, only entities of this type will get detached + * + * @return void + */ + public function clear($entityName = null) + { + $this->unitOfWork->clear($entityName); + } + + /** + * {@inheritDoc} + */ + 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 $entity The instance to make managed and persistent. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function persist($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()' , $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. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function remove($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()' , $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. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function refresh($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()' , $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. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function detach($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#detach()' , $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. + * + * @throws ORMInvalidArgumentException + */ + public function merge($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()' , $entity); + } + + $this->errorIfClosed(); + + return $this->unitOfWork->merge($entity); + } + + /** + * {@inheritDoc} + * + * @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."); + } + + /** + * {@inheritDoc} + */ + 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 \Doctrine\ORM\EntityRepository The repository class. + */ + public function getRepository($entityName) + { + return $this->repositoryFactory->getRepository($this, $entityName); + } + + /** + * 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); + } + + /** + * {@inheritDoc} + */ + public function getEventManager() + { + return $this->eventManager; + } + + /** + * {@inheritDoc} + */ + public function getConfiguration() + { + return $this->config; + } + + /** + * Throws an exception if the EntityManager is closed or currently not active. + * + * @return void + * + * @throws ORMException If the EntityManager is closed. + */ + private function errorIfClosed() + { + if ($this->closed) { + throw ORMException::entityManagerClosed(); + } + } + + /** + * {@inheritDoc} + */ + public function isOpen() + { + return (!$this->closed); + } + + /** + * {@inheritDoc} + */ + public function getUnitOfWork() + { + return $this->unitOfWork; + } + + /** + * {@inheritDoc} + */ + public function getHydrator($hydrationMode) + { + return $this->newHydrator($hydrationMode); + } + + /** + * {@inheritDoc} + */ + 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)) !== null) { + return new $class($this); + } + } + + throw ORMException::invalidHydrationMode($hydrationMode); + } + + /** + * {@inheritDoc} + */ + public function getProxyFactory() + { + return $this->proxyFactory; + } + + /** + * {@inheritDoc} + */ + 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. + * + * @throws \InvalidArgumentException + * @throws ORMException + */ + 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()); + } + + /** + * {@inheritDoc} + */ + public function getFilters() + { + if (null === $this->filterCollection) { + $this->filterCollection = new FilterCollection($this); + } + + return $this->filterCollection; + } + + /** + * {@inheritDoc} + */ + public function isFiltersStateClean() + { + return null === $this->filterCollection || $this->filterCollection->isClean(); + } + + /** + * {@inheritDoc} + */ + public function hasFilters() + { + return null !== $this->filterCollection; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php new file mode 100644 index 00000000000..016cfd4ec8b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php @@ -0,0 +1,296 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * EntityManager interface + * + * @since 2.4 + * @author Lars Strojny + * + * @method Mapping\ClassMetadata getClassMetadata($className) + */ +interface EntityManagerInterface extends ObjectManager +{ + /** + * Returns the cache API for managing the second level cache regions or NULL if the cache is not enabled. + * + * @return \Doctrine\ORM\Cache|null + */ + public function getCache(); + + /** + * Gets the database connection object used by the EntityManager. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection(); + + /** + * 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(); + + /** + * Starts a transaction on the underlying database connection. + * + * @return void + */ + public function 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 callable $func The function to execute transactionally. + * + * @return mixed The non-empty value returned from the closure or true instead. + */ + public function transactional($func); + + /** + * Commits a transaction on the underlying database connection. + * + * @return void + */ + public function commit(); + + /** + * Performs a rollback on the underlying database connection. + * + * @return void + */ + public function rollback(); + + /** + * Creates a new Query object. + * + * @param string $dql The DQL string. + * + * @return Query + */ + public function createQuery($dql = ''); + + /** + * Creates a Query from a named query. + * + * @param string $name + * + * @return Query + */ + public function createNamedQuery($name); + + /** + * Creates a native SQL query. + * + * @param string $sql + * @param ResultSetMapping $rsm The ResultSetMapping to use. + * + * @return NativeQuery + */ + public function createNativeQuery($sql, ResultSetMapping $rsm); + + /** + * Creates a NativeQuery from a named native query. + * + * @param string $name + * + * @return NativeQuery + */ + public function createNamedNativeQuery($name); + + /** + * Create a QueryBuilder instance + * + * @return QueryBuilder + */ + public function createQueryBuilder(); + + /** + * 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. + * + * @throws ORMException + */ + public function getReference($entityName, $id); + + /** + * 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); + + /** + * 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. + * + * @return void + */ + public function close(); + + /** + * Creates a copy of the given entity. Can create a shallow or a deep copy. + * + * @param object $entity The entity to copy. + * @param boolean $deep FALSE for a shallow copy, TRUE for a deep copy. + * + * @return object The new entity. + * + * @throws \BadMethodCallException + */ + public function copy($entity, $deep = false); + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int|null $lockVersion + * + * @return void + * + * @throws OptimisticLockException + * @throws PessimisticLockException + */ + public function lock($entity, $lockMode, $lockVersion = null); + + /** + * Gets the EventManager used by the EntityManager. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager(); + + /** + * Gets the Configuration used by the EntityManager. + * + * @return Configuration + */ + public function getConfiguration(); + + /** + * Check if the Entity manager is open or closed. + * + * @return bool + */ + public function isOpen(); + + /** + * Gets the UnitOfWork used by the EntityManager to coordinate operations. + * + * @return UnitOfWork + */ + public function getUnitOfWork(); + + /** + * 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. + * + * @deprecated + * + * @param int $hydrationMode + * + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + public function getHydrator($hydrationMode); + + /** + * Create a new instance for the given hydration mode. + * + * @param int $hydrationMode + * + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + * + * @throws ORMException + */ + public function newHydrator($hydrationMode); + + /** + * Gets the proxy factory used by the EntityManager to create entity proxies. + * + * @return \Doctrine\ORM\Proxy\ProxyFactory + */ + public function getProxyFactory(); + + /** + * Gets the enabled filters. + * + * @return \Doctrine\ORM\Query\FilterCollection The active filter collection. + */ + public function getFilters(); + + /** + * Checks whether the state of the filter collection is clean. + * + * @return boolean True, if the filter collection is clean. + */ + public function isFiltersStateClean(); + + /** + * Checks whether the Entity Manager has filters. + * + * @return boolean True, if the EM has a filter collection. + */ + public function hasFilters(); +} 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 00000000000..afe2f22426d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when a Proxy fails to retrieve an Entity result. + * + * @author robo + * @since 2.0 + */ +class EntityNotFoundException extends ORMException +{ + /** + * Static constructor. + * + * @param string $className + * @param string[] $id + * + * @return self + */ + public static function fromClassNameAndIdentifier($className, array $id) + { + $ids = array(); + + foreach ($id as $key => $value) { + $ids[] = $key . '(' . $value . ')'; + } + + + return new self( + 'Entity of type \'' . $className . '\'' . ($ids ? ' for IDs ' . implode(', ', $ids) : '') . ' was not found' + ); + } +} 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 00000000000..47d0648bead --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php @@ -0,0 +1,306 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Query\ResultSetMappingBuilder; +use Doctrine\Common\Persistence\ObjectRepository; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * 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, Selectable +{ + /** + * @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 Mapping\ClassMetadata $class The class descriptor. + */ + public function __construct($em, Mapping\ClassMetadata $class) + { + $this->_entityName = $class->name; + $this->_em = $em; + $this->_class = $class; + } + + /** + * Creates a new QueryBuilder instance that is prepopulated for this entity name. + * + * @param string $alias + * @param string $indexBy The index for the from. + * + * @return QueryBuilder + */ + public function createQueryBuilder($alias, $indexBy = null) + { + return $this->_em->createQueryBuilder() + ->select($alias) + ->from($this->_entityName, $alias, $indexBy); + } + + /** + * Creates a new result set mapping builder for this entity. + * + * The column naming strategy is "INCREMENT". + * + * @param string $alias + * + * @return ResultSetMappingBuilder + */ + public function createResultSetMappingBuilder($alias) + { + $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); + $rsm->addRootEntityFromClassMetadata($this->_entityName, $alias); + + return $rsm; + } + + /** + * Creates 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)); + } + + /** + * Creates a native SQL query. + * + * @param string $queryName + * + * @return NativeQuery + */ + public function createNativeNamedQuery($queryName) + { + $queryMapping = $this->_class->getNamedNativeQuery($queryName); + $rsm = new Query\ResultSetMappingBuilder($this->_em); + $rsm->addNamedNativeQueryMapping($this->_class, $queryMapping); + + return $this->_em->createNativeQuery($queryMapping['query'], $rsm); + } + + /** + * Clears the repository, causing all managed entities to become detached. + * + * @return void + */ + public function clear() + { + $this->_em->clear($this->_class->rootEntityName); + } + + /** + * Finds an entity by its primary key / identifier. + * + * @param mixed $id The identifier. + * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * during the search. + * @param int|null $lockVersion The lock version. + * + * @return object|null The entity instance or NULL if the entity can not be found. + */ + public function find($id, $lockMode = null, $lockVersion = null) + { + return $this->_em->find($this->_entityName, $id, $lockMode, $lockVersion); + } + + /** + * 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) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return $persister->loadAll($criteria, $orderBy, $limit, $offset); + } + + /** + * Finds a single entity by a set of criteria. + * + * @param array $criteria + * @param array|null $orderBy + * + * @return object|null The entity instance or NULL if the entity can not be found. + */ + public function findOneBy(array $criteria, array $orderBy = null) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return $persister->load($criteria, null, null, array(), null, 1, $orderBy); + } + + /** + * Adds support for magic finders. + * + * @param string $method + * @param array $arguments + * + * @return array|object The found entity/entities. + * + * @throws ORMException + * @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 (0 === strpos($method, 'findBy')): + $by = substr($method, 6); + $method = 'findBy'; + break; + + case (0 === strpos($method, 'findOneBy')): + $by = substr($method, 9); + $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)) { + switch (count($arguments)) { + case 1: + return $this->$method(array($fieldName => $arguments[0])); + + case 2: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1]); + + case 3: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1], $arguments[2]); + + case 4: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1], $arguments[2], $arguments[3]); + + default: + // Do nothing + } + } + + 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; + } + + /** + * Select all elements from a selectable that match the expression and + * return a new collection containing these elements. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return \Doctrine\Common\Collections\Collection + */ + public function matching(Criteria $criteria) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return new LazyCriteriaCollection($persister, $criteria); + } +} 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 00000000000..9a5c8cf0624 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php @@ -0,0 +1,54 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\Persistence\Event\LifecycleEventArgs as BaseLifecycleEventArgs; + +/** + * 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 BaseLifecycleEventArgs +{ + /** + * Retrieves associated Entity. + * + * @return object + */ + public function getEntity() + { + return $this->getObject(); + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->getObjectManager(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php new file mode 100644 index 00000000000..bbcbd4118eb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * A method invoker based on entity lifecycle. + * + * @author Fabio B. Silva + * @since 2.4 + */ +class ListenersInvoker +{ + const INVOKE_NONE = 0; + const INVOKE_LISTENERS = 1; + const INVOKE_CALLBACKS = 2; + const INVOKE_MANAGER = 4; + + /** + * @var \Doctrine\ORM\Mapping\EntityListenerResolver The Entity listener resolver. + */ + private $resolver; + + /** + * The EventManager used for dispatching events. + * + * @var \Doctrine\Common\EventManager + */ + private $eventManager; + + /** + * Initializes a new ListenersInvoker instance. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->eventManager = $em->getEventManager(); + $this->resolver = $em->getConfiguration()->getEntityListenerResolver(); + } + + /** + * Get the subscribed event systems + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $eventName The entity lifecycle event. + * + * @return integer Bitmask of subscribed event systems. + */ + public function getSubscribedSystems(ClassMetadata $metadata, $eventName) + { + $invoke = self::INVOKE_NONE; + + if (isset($metadata->lifecycleCallbacks[$eventName])) { + $invoke |= self::INVOKE_CALLBACKS; + } + + if (isset($metadata->entityListeners[$eventName])) { + $invoke |= self::INVOKE_LISTENERS; + } + + if ($this->eventManager->hasListeners($eventName)) { + $invoke |= self::INVOKE_MANAGER; + } + + return $invoke; + } + + /** + * Dispatches the lifecycle event of the given entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $eventName The entity lifecycle event. + * @param object $entity The Entity on which the event occurred. + * @param \Doctrine\Common\EventArgs $event The Event args. + * @param integer $invoke Bitmask to invoke listeners. + */ + public function invoke(ClassMetadata $metadata, $eventName, $entity, EventArgs $event, $invoke) + { + if($invoke & self::INVOKE_CALLBACKS) { + foreach ($metadata->lifecycleCallbacks[$eventName] as $callback) { + $entity->$callback($event); + } + } + + if($invoke & self::INVOKE_LISTENERS) { + foreach ($metadata->entityListeners[$eventName] as $listener) { + $class = $listener['class']; + $method = $listener['method']; + $instance = $this->resolver->resolve($class); + + $instance->$method($entity, $event); + } + } + + if($invoke & self::INVOKE_MANAGER) { + $this->eventManager->dispatchEvent($eventName, $event); + } + } +} \ 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 00000000000..95e75616e12 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs as BaseLoadClassMetadataEventArgs; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.0 + * + * Note: method annotations are used instead of method overrides (due to BC policy) + * + * @method __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata, \Doctrine\ORM\EntityManager $objectManager) + * @method \Doctrine\ORM\EntityManager getClassMetadata() + */ +class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs +{ + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->getObjectManager(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php new file mode 100644 index 00000000000..a044a7e321d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\Persistence\Event\ManagerEventArgs; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Class that holds event arguments for a `onClassMetadataNotFound` event. + * + * This object is mutable by design, allowing callbacks having access to it to set the + * found metadata in it, and therefore "cancelling" a `onClassMetadataNotFound` event + * + * @author Marco Pivetta + * @since 2.5 + */ +class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs +{ + /** + * @var string + */ + private $className; + + /** + * @var ClassMetadata|null + */ + private $foundMetadata; + + /** + * Constructor. + * + * @param string $className + * @param ObjectManager $objectManager + */ + public function __construct($className, ObjectManager $objectManager) + { + $this->className = (string) $className; + + parent::__construct($objectManager); + } + + /** + * @param ClassMetadata|null $classMetadata + */ + public function setFoundMetadata(ClassMetadata $classMetadata = null) + { + $this->foundMetadata = $classMetadata; + } + + /** + * @return ClassMetadata|null + */ + public function getFoundMetadata() + { + return $this->foundMetadata; + } + + /** + * Retrieve class name for which a failed metadata fetch attempt was executed + * + * @return string + */ + public function getClassName() + { + return $this->className; + } +} + 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 00000000000..dd827c72568 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the onClear event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * @var string + */ + private $entityClass; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + * @param string|null $entityClass Optional entity class. + */ + public function __construct(EntityManagerInterface $em, $entityClass = null) + { + $this->em = $em; + $this->entityClass = $entityClass; + } + + /** + * Retrieves 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|null + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Checks if event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} 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 00000000000..6a9c7c7f464 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnFlushEventArgs extends EventArgs +{ + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + +} 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 00000000000..860f2d33b90 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php @@ -0,0 +1,58 @@ +. + */ +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the postFlush event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Daniel Freudenberger + */ +class PostFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} 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 00000000000..d01a9263960 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class PreFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * @return \Doctrine\ORM\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 00000000000..d9a9f9db2cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * 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 EntityManagerInterface $em + * @param array $changeSet + */ + public function __construct($entity, EntityManagerInterface $em, array &$changeSet) + { + parent::__construct($entity, $em); + + $this->entityChangeSet = &$changeSet; + } + + /** + * Retrieves entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Checks if field has a changeset. + * + * @param string $field + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Gets 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]; + } + + /** + * Gets 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]; + } + + /** + * Sets the new value of this field. + * + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Asserts the field exists in changeset. + * + * @param string $field + * + * @return void + * + * @throws \InvalidArgumentException + */ + 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 00000000000..e16b47a4214 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php @@ -0,0 +1,167 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Container for all ORM events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + /** + * Private constructor. This class is not meant to be instantiated. + */ + 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 onClassMetadataNotFound event occurs whenever loading metadata for a class + * failed. + * + * @var string + */ + const onClassMetadataNotFound = 'onClassMetadataNotFound'; + + /** + * The preFlush event occurs when the EntityManager#flush() operation is invoked, + * but before any changes to managed entities 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'; +} 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 00000000000..5ed2872add6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +abstract class AbstractIdGenerator +{ + /** + * Generates an identifier for an entity. + * + * @param EntityManager|EntityManager $em + * @param \Doctrine\ORM\Mapping\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; + } +} 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 00000000000..447dbd6d597 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\ORMException; + +/** + * Special generator for application-assigned identifiers (doesn't 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. + * + * {@inheritDoc} + * + * @throws \Doctrine\ORM\ORMException + */ + public function generate(EntityManager $em, $entity) + { + $class = $em->getClassMetadata(get_class($entity)); + $idFields = $class->getIdentifierFieldNames(); + $identifier = array(); + + foreach ($idFields as $idField) { + $value = $class->getFieldValue($entity, $idField); + + if ( ! isset($value)) { + throw ORMException::entityMissingAssignedIdForField($entity, $idField); + } + + if (isset($class->associationMappings[$idField])) { + // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. + $value = $em->getUnitOfWork()->getSingleIdentifierValue($value); + } + + $identifier[$idField] = $value; + } + + return $identifier; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php new file mode 100644 index 00000000000..01d139f485b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php @@ -0,0 +1,66 @@ +. + */ + +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 BigIntegerIdentityGenerator extends AbstractIdGenerator +{ + /** + * The name of the sequence to pass to lastInsertId(), if any. + * + * @var string + */ + private $sequenceName; + + /** + * Constructor. + * + * @param string|null $sequenceName 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($sequenceName = null) + { + $this->sequenceName = $sequenceName; + } + + /** + * {@inheritDoc} + */ + public function generate(EntityManager $em, $entity) + { + return (string) $em->getConnection()->lastInsertId($this->sequenceName); + } + + /** + * {@inheritDoc} + */ + public function isPostInsertGenerator() + { + return true; + } +} + 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 00000000000..46cd7dbcd26 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php @@ -0,0 +1,66 @@ +. + */ + +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 +{ + /** + * The name of the sequence to pass to lastInsertId(), if any. + * + * @var string + */ + private $sequenceName; + + /** + * Constructor. + * + * @param string|null $sequenceName 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($sequenceName = null) + { + $this->sequenceName = $sequenceName; + } + + /** + * {@inheritDoc} + */ + public function generate( + EntityManager $em, $entity) + { + return (int)$em->getConnection()->lastInsertId($this->sequenceName); + } + + /** + * {@inheritdoc} + */ + public function isPostInsertGenerator() + { + return true; + } +} 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 00000000000..1236d95b551 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; +use Serializable; + +/** + * Represents an ID generator that uses a database sequence. + * + * @since 2.0 + * @author Roman Borschel + */ +class SequenceGenerator extends AbstractIdGenerator implements Serializable +{ + /** + * The allocation size of the sequence. + * + * @var int + */ + private $_allocationSize; + + /** + * The name of the sequence. + * + * @var string + */ + private $_sequenceName; + + /** + * @var int + */ + private $_nextValue = 0; + + /** + * @var int|null + */ + private $_maxValue = null; + + /** + * Initializes a new sequence generator. + * + * @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; + } + + /** + * {@inheritDoc} + */ + 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|null + */ + public function getCurrentMaxValue() + { + return $this->_maxValue; + } + + /** + * Gets the next value that will be returned by generate(). + * + * @return integer + */ + public function getNextValue() + { + return $this->_nextValue; + } + + /** + * @return string + */ + public function serialize() + { + return serialize(array( + 'allocationSize' => $this->_allocationSize, + 'sequenceName' => $this->_sequenceName + )); + } + + /** + * @param string $serialized + * + * @return void + */ + 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 00000000000..abf3ab8acd3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php @@ -0,0 +1,109 @@ +. + */ + +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 +{ + /** + * @var string + */ + private $_tableName; + + /** + * @var string + */ + private $_sequenceName; + + /** + * @var int + */ + private $_allocationSize; + + /** + * @var int|null + */ + private $_nextValue; + + /** + * @var int|null + */ + private $_maxValue; + + /** + * @param string $tableName + * @param string $sequenceName + * @param int $allocationSize + */ + public function __construct($tableName, $sequenceName = 'default', $allocationSize = 10) + { + $this->_tableName = $tableName; + $this->_sequenceName = $sequenceName; + $this->_allocationSize = $allocationSize; + } + + /** + * {@inheritDoc} + */ + 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++; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php new file mode 100644 index 00000000000..a8e92bbe202 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Represents an ID generator that uses the database UUID expression + * + * @since 2.3 + * @author Maarten de Keizer + */ +class UuidGenerator extends AbstractIdGenerator +{ + /** + * {@inheritDoc} + */ + public function generate(EntityManager $em, $entity) + { + $conn = $em->getConnection(); + $sql = 'SELECT ' . $conn->getDatabasePlatform()->getGuidExpression(); + return $conn->query($sql)->fetchColumn(0); + } +} 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 00000000000..9e069b0e3aa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php @@ -0,0 +1,156 @@ +. + */ + +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; + + /** + * @var array + */ + private $_nodeStates = array(); + + /** + * The nodes to sort. + * + * @var array + */ + private $_classes = array(); + + /** + * @var array + */ + private $_relatedClasses = array(); + + /** + * @var array + */ + private $_sorted = array(); + + /** + * Clears the current graph. + * + * @return void + */ + public function clear() + { + $this->_classes = array(); + $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; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $node + * + * @return void + */ + 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; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $fromClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $toClass + * + * @return void + */ + public function addDependency($fromClass, $toClass) + { + $this->_relatedClasses[$fromClass->name][] = $toClass; + } + + /** + * @param string $className + * + * @return bool + */ + public function hasClass($className) + { + return isset($this->_classes[$className]); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + public function addClass($class) + { + $this->_classes[$class->name] = $class; + } +} 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 00000000000..af232d1ff2e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -0,0 +1,470 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Events; +use Doctrine\ORM\Mapping\ClassMetadata; +use PDO; + +/** + * 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 +{ + /** + * The ResultSetMapping. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + protected $_rsm; + + /** + * The EntityManager instance. + * + * @var EntityManagerInterface + */ + protected $_em; + + /** + * The dbms Platform instance. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * The UnitOfWork of the associated EntityManager. + * + * @var \Doctrine\ORM\UnitOfWork + */ + protected $_uow; + + /** + * Local ClassMetadata cache to avoid going to the EntityManager all the time. + * + * @var array + */ + protected $_metadataCache = array(); + + /** + * The cache used during row-by-row hydration. + * + * @var array + */ + protected $_cache = array(); + + /** + * The statement that provides the data to hydrate. + * + * @var \Doctrine\DBAL\Driver\Statement + */ + protected $_stmt; + + /** + * The query hints. + * + * @var array + */ + protected $_hints; + + /** + * Initializes a new instance of a class derived from AbstractHydrator. + * + * @param EntityManagerInterface $em The EntityManager to use. + */ + public function __construct(EntityManagerInterface $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 + * @param array $hints + * + * @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 + * @param array $hints + * + * @return array + */ + 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, $result); + + return $result; + } + + /** + * When executed in a hydrate() loop we have to clear internal state to + * decrease memory consumption. + * + * @param mixed $eventArgs + * + * @return void + */ + public function onClear($eventArgs) + { + } + + /** + * Executes one-time preparation tasks, once each time hydration is started + * through {@link hydrateAll} or {@link iterate()}. + * + * @return void + */ + protected function prepare() + { + } + + /** + * Executes one-time cleanup tasks at the end of a hydration that was initiated + * through {@link hydrateAll} or {@link iterate()}. + * + * @return void + */ + protected function cleanup() + { + $this->_stmt->closeCursor(); + + $this->_stmt = null; + $this->_rsm = null; + $this->_cache = array(); + $this->_metadataCache = array(); + } + + /** + * Hydrates a single row from the current statement instance. + * + * Template method. + * + * @param array $data The row data. + * @param array $result The result to fill. + * + * @return void + * + * @throws HydrationException + */ + protected function hydrateRowData(array $data, array &$result) + { + throw new HydrationException("hydrateRowData() not implemented by this hydrator."); + } + + /** + * Hydrates all rows from the current statement instance at once. + * + * @return array + */ + 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 specific key 'scalars'. + * + * @param array $data SQL Result Row. + * @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 &$id, array &$nonemptyComponents) + { + $rowData = array('data' => array()); + + foreach ($data as $key => $value) { + if (($cacheKeyInfo = $this->hydrateColumnInfo($key)) === null) { + continue; + } + + $fieldName = $cacheKeyInfo['fieldName']; + + switch (true) { + case (isset($cacheKeyInfo['isNewObjectParameter'])): + $argIndex = $cacheKeyInfo['argIndex']; + $objIndex = $cacheKeyInfo['objIndex']; + $type = $cacheKeyInfo['type']; + $value = $type->convertToPHPValue($value, $this->_platform); + + $rowData['newObjects'][$objIndex]['class'] = $cacheKeyInfo['class']; + $rowData['newObjects'][$objIndex]['args'][$argIndex] = $value; + break; + + case (isset($cacheKeyInfo['isScalar'])): + $type = $cacheKeyInfo['type']; + $value = $type->convertToPHPValue($value, $this->_platform); + + $rowData['scalars'][$fieldName] = $value; + break; + + //case (isset($cacheKeyInfo['isMetaColumn'])): + default: + $dqlAlias = $cacheKeyInfo['dqlAlias']; + $type = $cacheKeyInfo['type']; + + // in an inheritance hierarchy the same field could be defined several times. + // We overwrite this value so long we don't 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['data'][$dqlAlias][$fieldName])) { + break; + } + + $rowData['data'][$dqlAlias][$fieldName] = $type + ? $type->convertToPHPValue($value, $this->_platform) + : $value; + + if ($cacheKeyInfo['isIdentifier'] && $value !== null) { + $id[$dqlAlias] .= '|' . $value; + $nonemptyComponents[$dqlAlias] = true; + } + break; + } + } + + 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 + * + * @return array The processed row. + */ + protected function gatherScalarRowData(&$data) + { + $rowData = array(); + + foreach ($data as $key => $value) { + if (($cacheKeyInfo = $this->hydrateColumnInfo($key)) === null) { + continue; + } + + $fieldName = $cacheKeyInfo['fieldName']; + + // WARNING: BC break! We know this is the desired behavior to type convert values, but this + // erroneous behavior exists since 2.0 and we're forced to keep compatibility. + if ( ! isset($cacheKeyInfo['isScalar'])) { + $dqlAlias = $cacheKeyInfo['dqlAlias']; + $type = $cacheKeyInfo['type']; + $fieldName = $dqlAlias . '_' . $fieldName; + $value = $type + ? $type->convertToPHPValue($value, $this->_platform) + : $value; + } + + $rowData[$fieldName] = $value; + } + + return $rowData; + } + + /** + * Retrieve column information from ResultSetMapping. + * + * @param string $key Column name + * + * @return array|null + */ + protected function hydrateColumnInfo($key) + { + if (isset($this->_cache[$key])) { + return $this->_cache[$key]; + } + + switch (true) { + // NOTE: Most of the times it's a field mapping, so keep it first!!! + case (isset($this->_rsm->fieldMappings[$key])): + $classMetadata = $this->getClassMetadata($this->_rsm->declaringClasses[$key]); + $fieldName = $this->_rsm->fieldMappings[$key]; + $fieldMapping = $classMetadata->fieldMappings[$fieldName]; + + return $this->_cache[$key] = array( + 'isIdentifier' => in_array($fieldName, $classMetadata->identifier), + 'fieldName' => $fieldName, + 'type' => Type::getType($fieldMapping['type']), + 'dqlAlias' => $this->_rsm->columnOwnerMap[$key], + ); + + case (isset($this->_rsm->newObjectMappings[$key])): + // WARNING: A NEW object is also a scalar, so it must be declared before! + $mapping = $this->_rsm->newObjectMappings[$key]; + + return $this->_cache[$key] = array( + 'isScalar' => true, + 'isNewObjectParameter' => true, + 'fieldName' => $this->_rsm->scalarMappings[$key], + 'type' => Type::getType($this->_rsm->typeMappings[$key]), + 'argIndex' => $mapping['argIndex'], + 'objIndex' => $mapping['objIndex'], + 'class' => new \ReflectionClass($mapping['className']), + ); + + case (isset($this->_rsm->scalarMappings[$key])): + return $this->_cache[$key] = array( + 'isScalar' => true, + 'fieldName' => $this->_rsm->scalarMappings[$key], + 'type' => Type::getType($this->_rsm->typeMappings[$key]), + ); + + 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]; + $dqlAlias = $this->_rsm->columnOwnerMap[$key]; + $classMetadata = $this->getClassMetadata($this->_rsm->aliasMap[$dqlAlias]); + $type = isset($this->_rsm->typeMappings[$key]) + ? Type::getType($this->_rsm->typeMappings[$key]) + : null; + + return $this->_cache[$key] = array( + 'isIdentifier' => isset($this->_rsm->isIdentifierColumn[$dqlAlias][$key]), + 'isMetaColumn' => true, + 'fieldName' => $fieldName, + 'type' => $type, + 'dqlAlias' => $dqlAlias, + ); + } + + // 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. + return null; + } + + /** + * Retrieve ClassMetadata associated to entity class name. + * + * @param string $className + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + protected function getClassMetadata($className) + { + if ( ! isset($this->_metadataCache[$className])) { + $this->_metadataCache[$className] = $this->_em->getClassMetadata($className); + } + + return $this->_metadataCache[$className]; + } + + /** + * Register entity as managed in UnitOfWork. + * + * @param ClassMetadata $class + * @param object $entity + * @param array $data + * + * @return void + * + * @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) { + $id[$fieldName] = isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName]; + } + } else { + $fieldName = $class->identifier[0]; + $id = array( + $fieldName => isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName] + ); + } + + $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); + } +} 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 00000000000..f3e4376f757 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -0,0 +1,307 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO; +use 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 +{ + /** + * @var array + */ + private $_rootAliases = array(); + + /** + * @var bool + */ + private $_isSimpleQuery = false; + + /** + * @var array + */ + private $_identifierMap = array(); + + /** + * @var array + */ + private $_resultPointers = array(); + + /** + * @var array + */ + private $_idTemplate = array(); + + /** + * @var int + */ + private $_resultCounter = 0; + + /** + * {@inheritdoc} + */ + protected function prepare() + { + $this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1; + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->_identifierMap[$dqlAlias] = array(); + $this->_resultPointers[$dqlAlias] = array(); + $this->_idTemplate[$dqlAlias] = ''; + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + + while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $row, array &$result) + { + // 1) Initialize + $id = $this->_idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + $rowData = $this->gatherRowData($row, $id, $nonemptyComponents); + + // 2) Now hydrate the data found in the current row. + foreach ($rowData['data'] 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]; + $parentClass = $this->_metadataCache[$this->_rsm->aliasMap[$parent]]; + $relation = $parentClass->associationMappings[$relationAlias]; + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + $oneToOne = false; + + if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = array(); + } + + if (isset($nonemptyComponents[$dqlAlias])) { + $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 { + $oneToOne = true; + + if ( + ( ! isset($nonemptyComponents[$dqlAlias])) && + ( ! isset($baseElement[$relationAlias])) + ) { + $baseElement[$relationAlias] = null; + } else if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = $data; + } + } + + $coll =& $baseElement[$relationAlias]; + + if (is_array($coll)) { + $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]) ) { + $result[] = $this->_rsm->isMixed + ? array($entityKey => null) + : null; + + $resultKey = $this->_resultCounter; + ++$this->_resultCounter; + + continue; + } + + // Check for an existing element + if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { + $element = $this->_rsm->isMixed + ? array($entityKey => $data) + : $data; + + 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; + } + + $this->updateResultPointer($result, $index, $dqlAlias, false); + } + } + + if ( ! isset($resultKey)) { + $this->_resultCounter++; + } + + // Append scalar values to mixed result sets + if (isset($rowData['scalars'])) { + if ( ! isset($resultKey)) { + // this only ever happens when no object is fetched (scalar result only) + $resultKey = isset($this->_rsm->indexByMap['scalars']) + ? $row[$this->_rsm->indexByMap['scalars']] + : $this->_resultCounter - 1; + } + + foreach ($rowData['scalars'] as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + + // Append new object to mixed result sets + if (isset($rowData['newObjects'])) { + if ( ! isset($resultKey)) { + $resultKey = $this->_resultCounter - 1; + } + + $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0); + + foreach ($rowData['newObjects'] as $objIndex => $newObject) { + $class = $newObject['class']; + $args = $newObject['args']; + $obj = $class->newInstanceArgs($args); + + if (count($args) == $scalarCount || ($scalarCount == 0 && count($rowData['newObjects']) == 1)) { + $result[$resultKey] = $obj; + + continue; + } + + $result[$resultKey][$objIndex] = $obj; + } + } + } + + /** + * 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. + * + * @return void + */ + private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne) + { + if ($coll === null) { + unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + + return; + } + + if ($oneToOne) { + $this->_resultPointers[$dqlAlias] =& $coll; + + return; + } + + if ($index !== false) { + $this->_resultPointers[$dqlAlias] =& $coll[$index]; + + return; + } + + if ( ! $coll) { + return; + } + + end($coll); + $this->_resultPointers[$dqlAlias] =& $coll[key($coll)]; + + return; + } +} 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 00000000000..b35012c994b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php @@ -0,0 +1,105 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +class HydrationException extends \Doctrine\ORM\ORMException +{ + /** + * @return HydrationException + */ + public static function nonUniqueResult() + { + return new self("The result returned by the query was not unique."); + } + + /** + * @param string $alias + * @param string $parentAlias + * + * @return HydrationException + */ + public static function parentObjectOfRelationNotFound($alias, $parentAlias) + { + return new self("The parent object of entity result with alias '$alias' was not found." + . " The parent alias is '$parentAlias'."); + } + + /** + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function emptyDiscriminatorValue($dqlAlias) + { + return new self("The DQL alias '" . $dqlAlias . "' contains an entity ". + "of an inheritance hierarchy with an empty discriminator value. This means " . + "that the database contains inconsistent data with an empty " . + "discriminator value in a table row." + ); + } + + /** + * @since 2.3 + * + * @param string $entityName + * @param string $discrColumnName + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function missingDiscriminatorColumn($entityName, $discrColumnName, $dqlAlias) + { + return new self(sprintf( + 'The discriminator column "%s" is missing for "%s" using the DQL alias "%s".', + $discrColumnName, $entityName, $dqlAlias + )); + } + + /** + * @since 2.3 + * + * @param string $entityName + * @param string $discrColumnName + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function missingDiscriminatorMetaMappingColumn($entityName, $discrColumnName, $dqlAlias) + { + return new self(sprintf( + 'The meta mapping for the discriminator column "%s" is missing for "%s" using the DQL alias "%s".', + $discrColumnName, $entityName, $dqlAlias + )); + } + + /** + * @param string $discrValue + * @param array $discrMap + * + * @return HydrationException + */ + public static function invalidDiscriminatorValue($discrValue, $discrMap) + { + return new self(sprintf( + 'The discriminator value "%s" is invalid. It must be one of "%s".', + $discrValue, implode('", "', $discrMap) + )); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php new file mode 100644 index 00000000000..3bbf6724d1d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php @@ -0,0 +1,109 @@ +. + */ + +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|null + */ + private $_current = null; + + /** + * @param \Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator + */ + public function __construct($hydrator) + { + $this->_hydrator = $hydrator; + } + + /** + * @return void + * + * @throws HydrationException + */ + 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); + } +} 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 00000000000..3cedaada0c3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -0,0 +1,595 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\ORM\UnitOfWork; +use PDO; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Query; +use Doctrine\ORM\Events; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\PostLoadEventDispatcher; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Proxy\Proxy; + +/** + * The ObjectHydrator constructs an object graph out of an SQL result set. + * + * Internal note: Highly performance-sensitive code. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + * @author Fabio B. Silva + */ +class ObjectHydrator extends AbstractHydrator +{ + /** + * @var array + */ + private $identifierMap = array(); + + /** + * @var array + */ + private $resultPointers = array(); + + /** + * @var array + */ + private $idTemplate = array(); + + /** + * @var integer + */ + private $resultCounter = 0; + + /** + * @var array + */ + private $rootAliases = array(); + + /** + * @var array + */ + private $initializedCollections = array(); + + /** + * @var array + */ + private $existingCollections = array(); + + /** + * {@inheritdoc} + */ + protected function prepare() + { + if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) { + $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true; + } + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->identifierMap[$dqlAlias] = array(); + $this->idTemplate[$dqlAlias] = ''; + + // 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; + } + + $parent = $this->_rsm->parentAliasMap[$dqlAlias]; + + if ( ! isset($this->_rsm->aliasMap[$parent])) { + throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent); + } + + $sourceClassName = $this->_rsm->aliasMap[$parent]; + $sourceClass = $this->getClassMetadata($sourceClassName); + $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; + + $this->_hints['fetched'][$parent][$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->getClassMetadata($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[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true; + + parent::cleanup(); + + $this->identifierMap = + $this->initializedCollections = + $this->existingCollections = + $this->resultPointers = array(); + + if ($eagerLoad) { + $this->_uow->triggerEagerLoads(); + } + + $this->_uow->hydrationComplete(); + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $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 $fieldName The name of the field on the entity that holds the collection. + * @param string $parentDqlAlias Alias of the parent fetch joining this collection. + * + * @return \Doctrine\ORM\PersistentCollection + */ + 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 || is_array($value)) { + $value = new ArrayCollection((array) $value); + } + + if ( ! $value instanceof PersistentCollection) { + $value = new PersistentCollection( + $this->_em, $this->_metadataCache[$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 array $data The instance data. + * @param string $dqlAlias The DQL alias of the entity's class. + * + * @return object The entity. + * + * @throws HydrationException + */ + private function getEntity(array $data, $dqlAlias) + { + $className = $this->_rsm->aliasMap[$dqlAlias]; + + if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) { + $fieldName = $this->_rsm->discriminatorColumns[$dqlAlias]; + + if ( ! isset($this->_rsm->metaMappings[$fieldName])) { + throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias); + } + + $discrColumn = $this->_rsm->metaMappings[$fieldName]; + + if ( ! isset($data[$discrColumn])) { + throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias); + } + + if ($data[$discrColumn] === "") { + throw HydrationException::emptyDiscriminatorValue($dqlAlias); + } + + $discrMap = $this->_metadataCache[$className]->discriminatorMap; + + if ( ! isset($discrMap[$data[$discrColumn]])) { + throw HydrationException::invalidDiscriminatorValue($data[$discrColumn], array_keys($discrMap)); + } + + $className = $discrMap[$data[$discrColumn]]; + + unset($data[$discrColumn]); + } + + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) { + $this->registerManaged($this->_metadataCache[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + + $this->_hints['fetchAlias'] = $dqlAlias; + + return $this->_uow->createEntity($className, $data, $this->_hints); + } + + /** + * @param string $className + * @param array $data + * + * @return mixed + */ + private function getEntityFromIdentityMap($className, array $data) + { + // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? + $class = $this->_metadataCache[$className]; + + /* @var $class ClassMetadata */ + if ($class->isIdentifierComposite) { + $idHash = ''; + + foreach ($class->identifier as $fieldName) { + $idHash .= ' ' . (isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName]); + } + + return $this->_uow->tryGetByIdHash(ltrim($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); + } + + return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName); + } + + /** + * 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 $row The data of the row to process. + * @param array $result The result array to fill. + * + * @return void + */ + protected function hydrateRowData(array $row, 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, $id, $nonemptyComponents); + + // Hydrate the data chunks + foreach ($rowData['data'] 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; + } + + $parentClass = $this->_metadataCache[$this->_rsm->aliasMap[$parentAlias]]; + $relationField = $this->_rsm->relationMap[$dqlAlias]; + $relation = $parentClass->associationMappings[$relationField]; + $reflField = $parentClass->reflFields[$relationField]; + + // 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, mark as not-fetched again + $element = $this->getEntity($data, $dqlAlias); + + // Update result pointer and provide initial fetch data for parent + $this->resultPointers[$dqlAlias] = $element; + $rowData['data'][$parentAlias][$relationField] = $element; + + // Mark as not-fetched again + unset($this->_hints['fetched'][$parentAlias][$relationField]); + continue; + } + + $oid = spl_object_hash($parentObject); + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + // PATH A: Collection-valued association + $reflFieldValue = $reflField->getValue($parentObject); + + 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->_metadataCache[$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); + $reflField->setValue($parentObject, 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($data, $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 (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) { + $this->_uow->hydrationComplete(); + } + } + + if ( ! isset($resultKey) ) { + $this->resultCounter++; + } + + // Append scalar values to mixed result sets + if (isset($rowData['scalars'])) { + if ( ! isset($resultKey) ) { + $resultKey = (isset($this->_rsm->indexByMap['scalars'])) + ? $row[$this->_rsm->indexByMap['scalars']] + : $this->resultCounter - 1; + } + + foreach ($rowData['scalars'] as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + + // Append new object to mixed result sets + if (isset($rowData['newObjects'])) { + if ( ! isset($resultKey) ) { + $resultKey = $this->resultCounter - 1; + } + + + $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0); + + foreach ($rowData['newObjects'] as $objIndex => $newObject) { + $class = $newObject['class']; + $args = $newObject['args']; + $obj = $class->newInstanceArgs($args); + + if ($scalarCount == 0 && count($rowData['newObjects']) == 1 ) { + $result[$resultKey] = $obj; + + continue; + } + + $result[$resultKey][$objIndex] = $obj; + } + } + } + + /** + * When executed in a hydrate() loop we may have to clear internal state to + * decrease memory consumption. + * + * @param mixed $eventArgs + * + * @return void + */ + 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 00000000000..024ee3b6692 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +/** + * 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(); + + while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $data, array &$result) + { + $result[] = $this->gatherScalarRowData($data); + } +} 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 00000000000..1c21369e346 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -0,0 +1,152 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; + +class SimpleObjectHydrator extends AbstractHydrator +{ + /** + * @var ClassMetadata + */ + private $class; + + /** + * {@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->getClassMetadata(reset($this->_rsm->aliasMap)); + } + + /** + * {@inheritdoc} + */ + protected function cleanup() + { + parent::cleanup(); + + $this->_uow->triggerEagerLoads(); + $this->_uow->hydrationComplete(); + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $result); + } + + $this->_em->getUnitOfWork()->triggerEagerLoads(); + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $sqlResult, 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']); + + // Find mapped discriminator column from the result set. + if ($metaMappingDiscrColumnName = array_search($discrColumnName, $this->_rsm->metaMappings)) { + $discrColumnName = $metaMappingDiscrColumnName; + } + + if ( ! isset($sqlResult[$discrColumnName])) { + throw HydrationException::missingDiscriminatorColumn($entityName, $discrColumnName, key($this->_rsm->aliasMap)); + } + + if ($sqlResult[$discrColumnName] === '') { + throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap)); + } + + $discrMap = $this->class->discriminatorMap; + + if ( ! isset($discrMap[$sqlResult[$discrColumnName]])) { + throw HydrationException::invalidDiscriminatorValue($sqlResult[$discrColumnName], array_keys($discrMap)); + } + + $entityName = $discrMap[$sqlResult[$discrColumnName]]; + + unset($sqlResult[$discrColumnName]); + } + + foreach ($sqlResult as $column => $value) { + // An ObjectHydrator should be used instead of SimpleObjectHydrator + if (isset($this->_rsm->relationMap[$column])) { + throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column)); + } + + $cacheKeyInfo = $this->hydrateColumnInfo($column); + + if ( ! $cacheKeyInfo) { + continue; + } + + // Convert field to a valid PHP value + if (isset($cacheKeyInfo['type'])) { + $type = $cacheKeyInfo['type']; + $value = $type->convertToPHPValue($value, $this->_platform); + } + + $fieldName = $cacheKeyInfo['fieldName']; + + // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator) + if ( ! isset($data[$fieldName]) || $value !== null) { + $data[$fieldName] = $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; + + if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) { + $this->_uow->hydrationComplete(); + } + } +} 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 00000000000..b9caeb11b83 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\ORM\NoResultException; +use 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) { + throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().'); + } + + if (count($data[key($data)]) > 1) { + throw new NonUniqueResultException('The query returned a row containing multiple columns. Change the query or use a different result function like getScalarResult().'); + } + + $result = $this->gatherScalarRowData($data[key($data)]); + + return array_shift($result); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php new file mode 100644 index 00000000000..4da71cefaf2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Internal; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\ListenersInvoker; +use Doctrine\ORM\Events; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Class, which can handle completion of hydration cycle and produce some of tasks. + * In current implementation triggers deferred postLoad event. + * + * @author Artur Eshenbrener + * @since 2.5 + */ +final class HydrationCompleteHandler +{ + /** + * @var ListenersInvoker + */ + private $listenersInvoker; + + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * @var array[] + */ + private $deferredPostLoadInvocations = array(); + + /** + * Constructor for this object + * + * @param ListenersInvoker $listenersInvoker + * @param EntityManagerInterface $em + */ + public function __construct(ListenersInvoker $listenersInvoker, EntityManagerInterface $em) + { + $this->listenersInvoker = $listenersInvoker; + $this->em = $em; + } + + /** + * Method schedules invoking of postLoad entity to the very end of current hydration cycle. + * + * @param ClassMetadata $class + * @param object $entity + */ + public function deferPostLoadInvoking(ClassMetadata $class, $entity) + { + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad); + + if ($invoke === ListenersInvoker::INVOKE_NONE) { + return; + } + + $this->deferredPostLoadInvocations[] = array($class, $invoke, $entity); + } + + /** + * This method should me called after any hydration cycle completed. + * + * Method fires all deferred invocations of postLoad events + */ + public function hydrationComplete() + { + $toInvoke = $this->deferredPostLoadInvocations; + $this->deferredPostLoadInvocations = array(); + + foreach ($toInvoke as $classAndEntity) { + list($class, $invoke, $entity) = $classAndEntity; + + $this->listenersInvoker->invoke( + $class, + Events::postLoad, + $entity, + new LifecycleEventArgs($entity, $this->em), + $invoke + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/LazyCriteriaCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/LazyCriteriaCollection.php new file mode 100644 index 00000000000..28e2709ac35 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/LazyCriteriaCollection.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\AbstractLazyCollection; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Selectable; +use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * A lazy collection that allow a fast count when using criteria object + * Once count gets executed once without collection being initialized, result + * is cached and returned on subsequent calls until collection gets loaded, + * then returning the number of loaded results. + * + * @since 2.5 + * @author Guilherme Blanco + * @author Michaël Gallego + */ +class LazyCriteriaCollection extends AbstractLazyCollection implements Selectable +{ + /** + * @var BasicEntityPersister + */ + protected $entityPersister; + + /** + * @var Criteria + */ + protected $criteria; + + /** + * @var integer|null + */ + private $count; + + /** + * @param EntityPersister $entityPersister + * @param Criteria $criteria + */ + public function __construct(EntityPersister $entityPersister, Criteria $criteria) + { + $this->entityPersister = $entityPersister; + $this->criteria = $criteria; + } + + /** + * Do an efficient count on the collection + * + * @return integer + */ + public function count() + { + if ($this->isInitialized()) { + return $this->collection->count(); + } + + // Return cached result in case count query was already executed + if ($this->count !== null) { + return $this->count; + } + + return $this->count = $this->entityPersister->count($this->criteria); + } + + /** + * Do an optimized search of an element + * + * @param object $element + * @return bool + */ + public function contains($element) + { + if ($this->isInitialized()) { + return $this->collection->contains($element); + } + + return $this->entityPersister->exists($element, $this->criteria); + } + + /** + * {@inheritDoc} + */ + public function matching(Criteria $criteria) + { + $this->initialize(); + + return $this->collection->matching($criteria); + } + + /** + * {@inheritDoc} + */ + protected function doInitialize() + { + $elements = $this->entityPersister->loadCriteria($this->criteria); + $this->collection = new ArrayCollection($elements); + } +} 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 00000000000..19374ff3146 --- /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/AnsiQuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php new file mode 100644 index 00000000000..d18c8be7c72 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * ANSI compliant quote strategy, this strategy does not apply any quote. + * To use this strategy all mapped tables and columns should be ANSI compliant. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class AnsiQuoteStrategy implements QuoteStrategy +{ + /** + * {@inheritdoc} + */ + public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform) + { + return $class->fieldMappings[$fieldName]['columnName']; + } + + /** + * {@inheritdoc} + */ + public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + { + return $class->table['name']; + } + + /** + * {@inheritdoc} + */ + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + { + return $definition['sequenceName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return $joinColumn['name']; + } + + /** + * {@inheritdoc} + */ + public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return $joinColumn['referencedColumnName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) + { + return $association['joinTable']['name']; + } + + /** + * {@inheritdoc} + */ + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + { + return $class->identifier; + } + + /** + * {@inheritdoc} + */ + public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) + { + return $platform->getSQLResultCasing($columnName . '_' . $counter); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php new file mode 100644 index 00000000000..1a9a31f1842 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mapping of property for an entity relationship. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AssociationOverride implements Annotation +{ + /** + * The name of the relationship property whose mapping is being overridden. + * + * @var string + */ + public $name; + + /** + * The join column that is being mapped to the persistent attribute. + * + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $joinColumns; + + /** + * The join table that maps the relationship. + * + * @var \Doctrine\ORM\Mapping\JoinTable + */ + public $joinTable; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php new file mode 100644 index 00000000000..217c9e45735 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mappings of relationship properties. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AssociationOverrides implements Annotation +{ + /** + * Mapping overrides of relationship properties. + * + * @var array<\Doctrine\ORM\Mapping\AssociationOverride> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php new file mode 100644 index 00000000000..f86d3a1521d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AttributeOverride implements Annotation +{ + /** + * The name of the property whose mapping is being overridden. + * + * @var string + */ + public $name; + + /** + * The column definition. + * + * @var \Doctrine\ORM\Mapping\Column + */ + public $column; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php new file mode 100644 index 00000000000..63b2cc66e7f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AttributeOverrides implements Annotation +{ + /** + * One or more field or property mapping overrides. + * + * @var array<\Doctrine\ORM\Mapping\AttributeOverride> + */ + public $value; +} 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 00000000000..5bc75e31ef6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata; + +class AssociationBuilder +{ + /** + * @var ClassMetadataBuilder + */ + protected $builder; + + /** + * @var array + */ + protected $mapping; + + /** + * @var array|null + */ + protected $joinColumns; + + /** + * @var int + */ + protected $type; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + * @param int $type + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping, $type) + { + $this->builder = $builder; + $this->mapping = $mapping; + $this->type = $type; + } + + /** + * @param string $fieldName + * + * @return AssociationBuilder + */ + public function mappedBy($fieldName) + { + $this->mapping['mappedBy'] = $fieldName; + return $this; + } + + /** + * @param string $fieldName + * + * @return AssociationBuilder + */ + public function inversedBy($fieldName) + { + $this->mapping['inversedBy'] = $fieldName; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeAll() + { + $this->mapping['cascade'] = array("ALL"); + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadePersist() + { + $this->mapping['cascade'][] = "persist"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeRemove() + { + $this->mapping['cascade'][] = "remove"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeMerge() + { + $this->mapping['cascade'][] = "merge"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeDetach() + { + $this->mapping['cascade'][] = "detach"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeRefresh() + { + $this->mapping['cascade'][] = "refresh"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchExtraLazy() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchEager() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EAGER; + return $this; + } + + /** + * @return AssociationBuilder + */ + 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|null $onDelete + * @param string|null $columnDef + * + * @return AssociationBuilder + */ + 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; + } + + /** + * Sets field as primary key. + * + * @return self + */ + public function makePrimaryKey() + { + $this->mapping['id'] = true; + + return $this; + } + + /** + * Removes orphan entities when detached from their parent. + * + * @return self + */ + public function orphanRemoval() + { + $this->mapping['orphanRemoval'] = true; + + return $this; + } + + /** + * @return ClassMetadataBuilder + * + * @throws \InvalidArgumentException + */ + 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 Association here"); + } + return $this->builder; + } +} 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 00000000000..7d771d3f95a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php @@ -0,0 +1,547 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Builder Object for ClassMetadata + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @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; + } + + /** + * Marks the class as mapped superclass. + * + * @return ClassMetadataBuilder + */ + public function setMappedSuperClass() + { + $this->cm->isMappedSuperclass = true; + $this->cm->isEmbeddedClass = false; + + return $this; + } + + /** + * Marks the class as embeddable. + * + * @return ClassMetadataBuilder + */ + public function setEmbeddable() + { + $this->cm->isEmbeddedClass = true; + $this->cm->isMappedSuperclass = false; + + return $this; + } + + /** + * Adds and embedded class + * + * @param string $fieldName + * @param string $class + * @param string|null $columnPrefix + * + * @return $this + */ + public function addEmbedded($fieldName, $class, $columnPrefix = null) + { + $this->cm->mapEmbedded(array( + 'fieldName' => $fieldName, + 'class' => $class, + 'columnPrefix' => $columnPrefix + )); + + return $this; + } + + /** + * Sets custom Repository class name. + * + * @param string $repositoryClassName + * + * @return ClassMetadataBuilder + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->cm->setCustomRepositoryClass($repositoryClassName); + + return $this; + } + + /** + * Marks class read only. + * + * @return ClassMetadataBuilder + */ + public function setReadOnly() + { + $this->cm->markReadOnly(); + + return $this; + } + + /** + * Sets the table name. + * + * @param string $name + * + * @return ClassMetadataBuilder + */ + public function setTable($name) + { + $this->cm->setPrimaryTable(array('name' => $name)); + + return $this; + } + + /** + * Adds 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; + } + + /** + * Adds 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; + } + + /** + * Adds 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; + } + + /** + * Sets class as root of a joined table inheritance hierarchy. + * + * @return ClassMetadataBuilder + */ + public function setJoinedTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED); + + return $this; + } + + /** + * Sets class as root of a single table inheritance hierarchy. + * + * @return ClassMetadataBuilder + */ + public function setSingleTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE); + + return $this; + } + + /** + * Sets the discriminator column details. + * + * @param string $name + * @param string $type + * @param int $length + * + * @return ClassMetadataBuilder + */ + public function setDiscriminatorColumn($name, $type = 'string', $length = 255) + { + $this->cm->setDiscriminatorColumn(array( + 'name' => $name, + 'type' => $type, + 'length' => $length, + )); + + return $this; + } + + /** + * Adds a subclass to this inheritance hierarchy. + * + * @param string $name + * @param string $class + * + * @return ClassMetadataBuilder + */ + public function addDiscriminatorMapClass($name, $class) + { + $this->cm->addDiscriminatorMapClass($name, $class); + + return $this; + } + + /** + * Sets deferred explicit change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyDeferredExplicit() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT); + + return $this; + } + + /** + * Sets notify change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyNotify() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY); + + return $this; + } + + /** + * Adds lifecycle event. + * + * @param string $methodName + * @param string $event + * + * @return ClassMetadataBuilder + */ + public function addLifecycleEvent($methodName, $event) + { + $this->cm->addLifecycleCallback($methodName, $event); + + return $this; + } + + /** + * Adds Field. + * + * @param string $name + * @param string $type + * @param array $mapping + * + * @return ClassMetadataBuilder + */ + public function addField($name, $type, array $mapping = array()) + { + $mapping['fieldName'] = $name; + $mapping['type'] = $type; + + $this->cm->mapField($mapping); + + return $this; + } + + /** + * Creates 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 + ) + ); + } + + /** + * Creates an embedded builder. + * + * @param string $fieldName + * @param string $class + * + * @return EmbeddedBuilder + */ + public function createEmbedded($fieldName, $class) + { + return new EmbeddedBuilder( + $this, + array( + 'fieldName' => $fieldName, + 'class' => $class, + 'columnPrefix' => null + ) + ); + } + + /** + * Adds 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(); + } + + /** + * Creates a ManyToOne Association 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 + ); + } + + /** + * Creates a OneToOne Association 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 + ); + } + + /** + * Adds simple inverse one-to-one association. + * + * @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(); + } + + /** + * Adds simple owning one-to-one association. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * + * @return ClassMetadataBuilder + */ + public function addOwningOneToOne($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createOneToOne($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Creates a ManyToMany Association 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 + ); + } + + /** + * Adds a simple owning many to many association. + * + * @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(); + } + + /** + * Adds a simple inverse many to many association. + * + * @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(); + } + + /** + * Creates a one to many association 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 + ); + } + + /** + * Adds simple OneToMany association. + * + * @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/EmbeddedBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EmbeddedBuilder.php new file mode 100644 index 00000000000..de8383c5357 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EmbeddedBuilder.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * Embedded Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.5 + * @author Guido Contreras Woda + */ +class EmbeddedBuilder +{ + /** + * @var ClassMetadataBuilder + */ + private $builder; + + /** + * @var array + */ + private $mapping; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping) + { + $this->builder = $builder; + $this->mapping = $mapping; + } + + /** + * Sets the column prefix for all of the embedded columns. + * + * @param string $columnPrefix + * @return $this + */ + public function setColumnPrefix($columnPrefix) + { + $this->mapping['columnPrefix'] = $columnPrefix; + + return $this; + } + + /** + * Finalizes this embeddable and attach it to the ClassMetadata. + * + * Without this call an EmbeddedBuilder has no effect on the ClassMetadata. + * + * @return ClassMetadataBuilder + */ + public function build() + { + $cm = $this->builder->getClassMetadata(); + + $cm->mapEmbedded($this->mapping); + + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php new file mode 100644 index 00000000000..d17abeac52c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Events; + +/** + * Builder for entity listeners. + * + * @since 2.4 + * @author Fabio B. Silva + */ +class EntityListenerBuilder +{ + /** + * @var array Hash-map to handle event names. + */ + static private $events = array( + Events::preRemove => true, + Events::postRemove => true, + Events::prePersist => true, + Events::postPersist => true, + Events::preUpdate => true, + Events::postUpdate => true, + Events::postLoad => true, + Events::preFlush => true + ); + + /** + * Lookup the entity class to find methods that match to event lifecycle names + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $className The listener class name. + * + * @throws \Doctrine\ORM\Mapping\MappingException When the listener class not found. + */ + static public function bindEntityListener(ClassMetadata $metadata, $className) + { + $class = $metadata->fullyQualifiedClassName($className); + + if ( ! class_exists($class)) { + throw MappingException::entityListenerClassNotFound($class, $className); + } + + foreach (get_class_methods($class) as $method) { + if ( ! isset(self::$events[$method])) { + continue; + } + + $metadata->addEntityListener($method, $class, $method); + } + } +} \ No newline at end of file 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 00000000000..425840aaf0d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php @@ -0,0 +1,257 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * Field Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @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; + } + + /** + * Sets length. + * + * @param int $length + * + * @return FieldBuilder + */ + public function length($length) + { + $this->mapping['length'] = $length; + return $this; + } + + /** + * Sets nullable. + * + * @param bool $flag + * + * @return FieldBuilder + */ + public function nullable($flag = true) + { + $this->mapping['nullable'] = (bool)$flag; + return $this; + } + + /** + * Sets Unique. + * + * @param bool $flag + * + * @return FieldBuilder + */ + public function unique($flag = true) + { + $this->mapping['unique'] = (bool)$flag; + return $this; + } + + /** + * Sets column name. + * + * @param string $name + * + * @return FieldBuilder + */ + public function columnName($name) + { + $this->mapping['columnName'] = $name; + return $this; + } + + /** + * Sets Precision. + * + * @param int $p + * + * @return FieldBuilder + */ + public function precision($p) + { + $this->mapping['precision'] = $p; + return $this; + } + + /** + * Sets scale. + * + * @param int $s + * + * @return FieldBuilder + */ + public function scale($s) + { + $this->mapping['scale'] = $s; + return $this; + } + + /** + * Sets field as primary key. + * + * @deprecated Use makePrimaryKey() instead + * @return FieldBuilder + */ + public function isPrimaryKey() + { + return $this->makePrimaryKey(); + } + + /** + * Sets field as primary key. + * + * @return FieldBuilder + */ + public function makePrimaryKey() + { + $this->mapping['id'] = true; + return $this; + } + + /** + * Sets an option. + * + * @param string $name + * @param mixed $value + * + * @return FieldBuilder + */ + public function option($name, $value) + { + $this->mapping['options'][$name] = $value; + return $this; + } + + /** + * @param string $strategy + * + * @return FieldBuilder + */ + public function generatedValue($strategy = 'AUTO') + { + $this->generatedValue = $strategy; + return $this; + } + + /** + * Sets field versioned. + * + * @return FieldBuilder + */ + public function isVersionField() + { + $this->version = true; + return $this; + } + + /** + * Sets 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; + } + + /** + * Sets column definition. + * + * @param string $def + * + * @return FieldBuilder + */ + public function columnDefinition($def) + { + $this->mapping['columnDefinition'] = $def; + return $this; + } + + /** + * Finalizes 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; + } +} 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 00000000000..a182e1e862b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * ManyToMany Association Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder +{ + /** + * @var string|null + */ + private $joinTableName; + + /** + * @var array + */ + private $inverseJoinColumns = array(); + + /** + * @param string $name + * + * @return ManyToManyAssociationBuilder + */ + public function setJoinTable($name) + { + $this->joinTableName = $name; + return $this; + } + + /** + * Adds Inverse Join Columns. + * + * @param string $columnName + * @param string $referencedColumnName + * @param bool $nullable + * @param bool $unique + * @param string|null $onDelete + * @param string|null $columnDef + * + * @return ManyToManyAssociationBuilder + */ + 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; + } +} 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 00000000000..4ca60f0ce03 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * OneToMany Association Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @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; + } + + /** + * @param string $fieldName + * + * @return OneToManyAssociationBuilder + */ + 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; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Cache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Cache.php new file mode 100644 index 00000000000..3226b603160 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Cache.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Caching to an entity or a collection. + * + * @author Fabio B. Silva + * @since 2.5 + * + * @Annotation + * @Target({"CLASS","PROPERTY"}) + */ +final class Cache implements Annotation +{ + /** + * @Enum({"READ_ONLY", "NONSTRICT_READ_WRITE", "READ_WRITE"}) + * + * @var string The concurrency strategy. + */ + public $usage = 'READ_ONLY'; + + /** + * @var string Cache region name. + */ + public $region; +} 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 00000000000..3657b764f1e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class ChangeTrackingPolicy implements Annotation +{ + /** + * The change tracking policy. + * + * @var string + * + * @Enum({"DEFERRED_IMPLICIT", "DEFERRED_EXPLICIT", "NOTIFY"}) + */ + 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 00000000000..a57f1e15a10 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * {@inheritDoc} + * + * @todo remove or rename ClassMetadataInfo to ClassMetadata + */ +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 00000000000..e0eacfa5317 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -0,0 +1,768 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; +use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; +use Doctrine\Common\Persistence\Mapping\ReflectionService; +use Doctrine\DBAL\Platforms; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; +use Doctrine\ORM\Events; +use Doctrine\ORM\Id\BigIntegerIdentityGenerator; +use Doctrine\ORM\Id\IdentityGenerator; +use Doctrine\ORM\ORMException; +use ReflectionException; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping information 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 extends AbstractClassMetadataFactory +{ + /** + * @var EntityManagerInterface|null + */ + private $em; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $targetPlatform; + + /** + * @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver + */ + private $driver; + + /** + * @var \Doctrine\Common\EventManager + */ + private $evm; + + /** + * @var array + */ + private $embeddablesActiveNesting = array(); + + /** + * {@inheritDoc} + */ + protected function loadMetadata($name) + { + $loaded = parent::loadMetadata($name); + + array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded)); + + return $loaded; + } + + /** + * @param EntityManagerInterface $em + */ + public function setEntityManager(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * {@inheritDoc} + */ + protected function initialize() + { + $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl(); + $this->evm = $this->em->getEventManager(); + $this->initialized = true; + } + + /** + * {@inheritDoc} + */ + protected function onNotFoundMetadata($className) + { + if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) { + return; + } + + $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em); + + $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs); + + return $eventArgs->getFoundMetadata(); + } + + /** + * {@inheritDoc} + */ + protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents) + { + /* @var $class ClassMetadata */ + /* @var $parent ClassMetadata */ + if ($parent) { + $class->setInheritanceType($parent->inheritanceType); + $class->setDiscriminatorColumn($parent->discriminatorColumn); + $class->setIdGeneratorType($parent->generatorType); + $this->addInheritedFields($class, $parent); + $this->addInheritedRelations($class, $parent); + $this->addInheritedEmbeddedClasses($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 ( ! empty($parent->customGeneratorDefinition)) { + $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition); + } + + if ($parent->isMappedSuperclass) { + $class->setCustomRepositoryClass($parent->customRepositoryClassName); + } + } + + // Invoke driver + try { + $this->driver->loadMetadataForClass($class->getName(), $class); + } catch (ReflectionException $e) { + throw MappingException::reflectionFailure($class->getName(), $e); + } + + // If this class has a parent the id generator strategy is inherited. + // However this is only true if the hierarchy of parents contains the root entity, + // if it consists 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->tableGeneratorDefinition = $parent->tableGeneratorDefinition; + } + + if ($parent->generatorType) { + $class->setIdGeneratorType($parent->generatorType); + } + + if ($parent->idGenerator) { + $class->setIdGenerator($parent->idGenerator); + } + } else { + $this->completeIdGeneratorMapping($class); + } + + if (!$class->isMappedSuperclass) { + foreach ($class->embeddedClasses as $property => $embeddableClass) { + + if (isset($embeddableClass['inherited'])) { + continue; + } + + if ( ! (isset($embeddableClass['class']) && $embeddableClass['class'])) { + throw MappingException::missingEmbeddedClass($property); + } + + if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) { + throw MappingException::infiniteEmbeddableNesting($class->name, $property); + } + + $this->embeddablesActiveNesting[$class->name] = true; + + $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); + + if ($embeddableMetadata->isEmbeddedClass) { + $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property); + } + + $class->inlineEmbeddable($property, $embeddableMetadata); + + unset($this->embeddablesActiveNesting[$class->name]); + } + } + + if ($parent) { + if ($parent->isInheritanceTypeSingleTable()) { + $class->setPrimaryTable($parent->table); + } + + if ($parent) { + $this->addInheritedIndexes($class, $parent); + } + + if ($parent->cache) { + $class->cache = $parent->cache; + } + + if ($parent->containsForeignIdentifier) { + $class->containsForeignIdentifier = true; + } + + if ( ! empty($parent->namedQueries)) { + $this->addInheritedNamedQueries($class, $parent); + } + + if ( ! empty($parent->namedNativeQueries)) { + $this->addInheritedNamedNativeQueries($class, $parent); + } + + if ( ! empty($parent->sqlResultSetMappings)) { + $this->addInheritedSqlResultSetMappings($class, $parent); + } + + if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) { + $class->entityListeners = $parent->entityListeners; + } + } + + $class->setParentClasses($nonSuperclassParents); + + if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) { + $this->addDefaultDiscriminatorMap($class); + } + + if ($this->evm->hasListeners(Events::loadClassMetadata)) { + $eventArgs = new LoadClassMetadataEventArgs($class, $this->em); + $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); + } + + $this->validateRuntimeMetadata($class, $parent); + } + + /** + * Validate runtime metadata is correctly defined. + * + * @param ClassMetadata $class + * @param ClassMetadataInterface|null $parent + * + * @return void + * + * @throws MappingException + */ + protected function validateRuntimeMetadata($class, $parent) + { + if ( ! $class->reflClass ) { + // only validate if there is a reflection class instance + return; + } + + $class->validateIdentifier(); + $class->validateAssociations(); + $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 ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { + // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy + throw MappingException::noInheritanceOnMappedSuperClass($class->name); + } + } + + /** + * {@inheritDoc} + */ + protected function newClassMetadataInstance($className) + { + return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy()); + } + + /** + * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator + * map classes and looking for a fitting one. + * + * @param ClassMetadata $metadata + * + * @return void + * + * @throws MappingException + */ + private function resolveDiscriminatorValue(ClassMetadata $metadata) + { + if ($metadata->discriminatorValue + || ! $metadata->discriminatorMap + || $metadata->isMappedSuperclass + || ! $metadata->reflClass + || $metadata->reflClass->isAbstract() + ) { + return; + } + + // minor optimization: avoid loading related metadata when not needed + foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { + if ($discriminatorClass === $metadata->name) { + $metadata->discriminatorValue = $discriminatorValue; + + return; + } + } + + // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata + foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { + if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) { + $metadata->discriminatorValue = $discriminatorValue; + + return; + } + } + + throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName); + } + + /** + * Adds a default discriminator map if no one is given + * + * If an entity is of any inheritance type and does not contain a + * discriminator map, then the map is generated automatically. This process + * is expensive computation wise. + * + * The automatically generated discriminator map contains the lowercase short name of + * each class as key. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @throws MappingException + */ + private function addDefaultDiscriminatorMap(ClassMetadata $class) + { + $allClasses = $this->driver->getAllClassNames(); + $fqcn = $class->getName(); + $map = array($this->getShortName($class->name) => $fqcn); + + $duplicates = array(); + foreach ($allClasses as $subClassCandidate) { + if (is_subclass_of($subClassCandidate, $fqcn)) { + $shortName = $this->getShortName($subClassCandidate); + + if (isset($map[$shortName])) { + $duplicates[] = $shortName; + } + + $map[$shortName] = $subClassCandidate; + } + } + + if ($duplicates) { + throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map); + } + + $class->setDiscriminatorMap($map); + } + + /** + * Gets the lower-case short name of a class. + * + * @param string $className + * + * @return string + */ + private function getShortName($className) + { + if (strpos($className, "\\") === false) { + return strtolower($className); + } + + $parts = explode("\\", $className); + return strtolower(end($parts)); + } + + /** + * Adds inherited fields to the subclass mapping. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->fieldMappings as $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 + * + * @return void + * + * @throws MappingException + */ + 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::illegalToManyAssociationOnMappedSuperclass($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); + } + } + + private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->embeddedClasses as $field => $embeddedClass) { + if ( ! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) { + $embeddedClass['inherited'] = $parentClass->name; + } + if ( ! isset($embeddedClass['declared'])) { + $embeddedClass['declared'] = $parentClass->name; + } + + $subClass->embeddedClasses[$field] = $embeddedClass; + } + } + + /** + * Adds nested embedded classes metadata to a parent class. + * + * @param ClassMetadata $subClass Sub embedded class metadata to add nested embedded classes metadata from. + * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to. + * @param string $prefix Embedded classes' prefix to use for nested embedded classes field names. + */ + private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix) + { + foreach ($subClass->embeddedClasses as $property => $embeddableClass) { + if (isset($embeddableClass['inherited'])) { + continue; + } + + $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); + + $parentClass->mapEmbedded(array( + 'fieldName' => $prefix . '.' . $property, + 'class' => $embeddableMetadata->name, + 'columnPrefix' => $embeddableClass['columnPrefix'], + 'declaredField' => $embeddableClass['declaredField'] + ? $prefix . '.' . $embeddableClass['declaredField'] + : $prefix, + 'originalField' => $embeddableClass['originalField'] ?: $property, + )); + } + } + + /** + * Copy the table indices from the parent class superclass to the child class + * + * @param ClassMetadata $subClass + * @param ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) + { + if (! $parentClass->isMappedSuperclass) { + return; + } + + foreach (array('uniqueConstraints', 'indexes') as $indexType) { + if (isset($parentClass->table[$indexType])) { + foreach ($parentClass->table[$indexType] as $indexName => $index) { + if (isset($subClass->table[$indexType][$indexName])) { + continue; // Let the inheriting table override indices + } + + $subClass->table[$indexType][$indexName] = $index; + } + } + } + } + + /** + * Adds inherited named queries to the subclass mapping. + * + * @since 2.2 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + 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'] + )); + } + } + } + + /** + * Adds inherited named native queries to the subclass mapping. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->namedNativeQueries as $name => $query) { + if ( ! isset ($subClass->namedNativeQueries[$name])) { + $subClass->addNamedNativeQuery(array( + 'name' => $query['name'], + 'query' => $query['query'], + 'isSelfClass' => $query['isSelfClass'], + 'resultSetMapping' => $query['resultSetMapping'], + 'resultClass' => $query['isSelfClass'] ? $subClass->name : $query['resultClass'], + )); + } + } + } + + /** + * Adds inherited sql result set mappings to the subclass mapping. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->sqlResultSetMappings as $name => $mapping) { + if ( ! isset ($subClass->sqlResultSetMappings[$name])) { + $entities = array(); + foreach ($mapping['entities'] as $entity) { + $entities[] = array( + 'fields' => $entity['fields'], + 'isSelfClass' => $entity['isSelfClass'], + 'discriminatorColumn' => $entity['discriminatorColumn'], + 'entityClass' => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'], + ); + } + + $subClass->addSqlResultSetMapping(array( + 'name' => $mapping['name'], + 'columns' => $mapping['columns'], + 'entities' => $entities, + )); + } + } + } + + /** + * Completes the ID generator mapping. If "auto" is specified we choose the generator + * most appropriate for the targeted database platform. + * + * @param ClassMetadataInfo $class + * + * @return void + * + * @throws ORMException + */ + private function completeIdGeneratorMapping(ClassMetadataInfo $class) + { + $idGenType = $class->generatorType; + if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) { + if ($this->getTargetPlatform()->prefersSequences()) { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); + } else if ($this->getTargetPlatform()->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: + $sequenceName = null; + $fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null; + + // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour. + if ($this->getTargetPlatform()->usesSequenceEmulatedIdentityColumns()) { + $columnName = $class->getSingleIdentifierColumnName(); + $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); + $sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform()); + $sequenceName = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName); + $definition = array( + 'sequenceName' => $this->getTargetPlatform()->fixSchemaElementName($sequenceName) + ); + + if ($quoted) { + $definition['quoted'] = true; + } + + $sequenceName = $this + ->em + ->getConfiguration() + ->getQuoteStrategy() + ->getSequenceName($definition, $class, $this->getTargetPlatform()); + } + + $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint') + ? new BigIntegerIdentityGenerator($sequenceName) + : new IdentityGenerator($sequenceName); + + $class->setIdGenerator($generator); + + break; + + case ClassMetadata::GENERATOR_TYPE_SEQUENCE: + // If there is no sequence definition yet, create a default definition + $definition = $class->sequenceGeneratorDefinition; + + if ( ! $definition) { + $fieldName = $class->getSingleIdentifierFieldName(); + $sequenceName = $class->getSequenceName($this->getTargetPlatform()); + $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); + + $definition = array( + 'sequenceName' => $this->getTargetPlatform()->fixSchemaElementName($sequenceName), + 'allocationSize' => 1, + 'initialValue' => 1, + ); + + if ($quoted) { + $definition['quoted'] = true; + } + + $class->setSequenceGeneratorDefinition($definition); + } + + $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator( + $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()), + $definition['allocationSize'] + ); + $class->setIdGenerator($sequenceGenerator); + break; + + case ClassMetadata::GENERATOR_TYPE_NONE: + $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator()); + break; + + case ClassMetadata::GENERATOR_TYPE_UUID: + $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator()); + break; + + case ClassMetadata::GENERATOR_TYPE_TABLE: + throw new ORMException("TableGenerator not yet implemented."); + break; + + case ClassMetadata::GENERATOR_TYPE_CUSTOM: + $definition = $class->customGeneratorDefinition; + if ( ! class_exists($definition['class'])) { + throw new ORMException("Can't instantiate custom generator : " . + $definition['class']); + } + $class->setIdGenerator(new $definition['class']); + break; + + default: + throw new ORMException("Unknown generator type: " . $class->generatorType); + } + } + + /** + * {@inheritDoc} + */ + protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + /* @var $class ClassMetadata */ + $class->wakeupReflection($reflService); + } + + /** + * {@inheritDoc} + */ + protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + /* @var $class ClassMetadata */ + $class->initializeReflection($reflService); + } + + /** + * {@inheritDoc} + */ + protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) + { + return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + /** + * {@inheritDoc} + */ + protected function getDriver() + { + return $this->driver; + } + + /** + * {@inheritDoc} + */ + protected function isEntity(ClassMetadataInterface $class) + { + return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false; + } + + /** + * @return Platforms\AbstractPlatform + */ + private function getTargetPlatform() + { + if (!$this->targetPlatform) { + $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform(); + } + + return $this->targetPlatform; + } +} 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 00000000000..79dcdb2cb27 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -0,0 +1,3317 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use BadMethodCallException; +use Doctrine\Instantiator\Instantiator; +use InvalidArgumentException; +use RuntimeException; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use ReflectionClass; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\ClassLoader; + +/** + * A ClassMetadata instance holds all the object-relational mapping metadata + * of an entity and its 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; + + /** + * UUID means that a UUID/GUID expression is used for id generation. Full + * portability is currently not guaranteed. + */ + const GENERATOR_TYPE_UUID = 6; + + /** + * CUSTOM means that customer will use own ID generator that supposedly work + */ + const GENERATOR_TYPE_CUSTOM = 7; + + /** + * 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; + + /** + * ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks, + */ + const CACHE_USAGE_READ_ONLY = 1; + + /** + * Nonstrict Read Write Cache doesn’t employ any locks but can do inserts, update and deletes. + */ + const CACHE_USAGE_NONSTRICT_READ_WRITE = 2; + + /** + * Read Write Attempts to lock the entity before update/delete. + */ + const CACHE_USAGE_READ_WRITE = 3; + + /** + * READ-ONLY: The name of the entity class. + * + * @var string + */ + 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; + + /** + * READ-ONLY: The definition of custom generator. Only used for CUSTOM + * generator type + * + * The definition has the following structure: + * + * array( + * 'class' => 'ClassName', + * ) + * + * + * @var array + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $customGeneratorDefinition; + + /** + * 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: Whether this class describes the mapping of an embeddable class. + * + * @var boolean + */ + public $isEmbeddedClass = 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 names of all embedded classes based on properties. + * + * @var array + */ + public $embeddedClasses = array(); + + /** + * READ-ONLY: The named queries allowed to be called directly from Repository. + * + * @var array + */ + public $namedQueries = array(); + + /** + * READ-ONLY: The named native queries allowed to be called directly from Repository. + * + * A native SQL named query definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'query'              => ,
+     *     'resultClass'        => ,
+     *     'resultSetMapping'   => 
+     * )
+     * 
+ * + * @var array + */ + public $namedNativeQueries = array(); + + /** + * READ-ONLY: The mappings of the results of native SQL queries. + * + * A native result mapping definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'entities'           => array(),
+     *     'columns'            => array()
+     * )
+     * 
+ * + * @var array + */ + public $sqlResultSetMappings = 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 int + */ + 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 registered entity listeners. + * + * @var array + */ + public $entityListeners = 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 whether 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 \Doctrine\ORM\Id\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 + */ + public $isVersioned; + + /** + * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). + * + * @var mixed + */ + public $versionField; + + /** + * @var array + */ + public $cache = null; + + /** + * 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; + + /** + * NamingStrategy determining the default column and table names. + * + * @var \Doctrine\ORM\Mapping\NamingStrategy + */ + protected $namingStrategy; + + /** + * The ReflectionProperty instances of the mapped class. + * + * @var \ReflectionProperty[] + */ + public $reflFields = array(); + + /** + * @var \Doctrine\Instantiator\InstantiatorInterface|null + */ + private $instantiator; + + /** + * 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. + * @param NamingStrategy|null $namingStrategy + */ + public function __construct($entityName, NamingStrategy $namingStrategy = null) + { + $this->name = $entityName; + $this->rootEntityName = $entityName; + $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy(); + $this->instantiator = new Instantiator(); + } + + /** + * Gets the ReflectionProperties 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; + } + + $id = $this->identifier[0]; + $value = $this->reflFields[$id]->getValue($entity); + + if (null === $value) { + return array(); + } + + return array($id => $value); + } + + /** + * Populates the entity identifier of an entity. + * + * @param object $entity + * @param mixed $id + * + * @return void + * + * @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 + * + * @return void + */ + 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 + * + * @return mixed + */ + 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', + 'embeddedClasses', + '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->isEmbeddedClass) { + $serialized[] = 'isEmbeddedClass'; + } + + if ($this->containsForeignIdentifier) { + $serialized[] = 'containsForeignIdentifier'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + if ($this->entityListeners) { + $serialized[] = 'entityListeners'; + } + + if ($this->namedQueries) { + $serialized[] = 'namedQueries'; + } + + if ($this->namedNativeQueries) { + $serialized[] = 'namedNativeQueries'; + } + + if ($this->sqlResultSetMappings) { + $serialized[] = 'sqlResultSetMappings'; + } + + if ($this->isReadOnly) { + $serialized[] = 'isReadOnly'; + } + + if ($this->customGeneratorDefinition) { + $serialized[] = "customGeneratorDefinition"; + } + + if ($this->cache) { + $serialized[] = 'cache'; + } + + return $serialized; + } + + /** + * Creates a new instance of the mapped class, without invoking the constructor. + * + * @return object + */ + public function newInstance() + { + return $this->instantiator->instantiate($this->name); + } + + /** + * Restores some state that can not be serialized/unserialized. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * + * @return void + */ + public function wakeupReflection($reflService) + { + // Restore ReflectionClass and properties + $this->reflClass = $reflService->getClass($this->name); + $this->instantiator = $this->instantiator ?: new Instantiator(); + + $parentReflFields = array(); + + foreach ($this->embeddedClasses as $property => $embeddedClass) { + if (isset($embeddedClass['declaredField'])) { + $parentReflFields[$property] = new ReflectionEmbeddedProperty( + $parentReflFields[$embeddedClass['declaredField']], + $reflService->getAccessibleProperty( + $this->embeddedClasses[$embeddedClass['declaredField']]['class'], + $embeddedClass['originalField'] + ), + $this->embeddedClasses[$embeddedClass['declaredField']]['class'] + ); + + continue; + } + + $parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property); + $this->reflFields[$property] = $reflService->getAccessibleProperty($this->name, $property); + } + + foreach ($this->fieldMappings as $field => $mapping) { + if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) { + $this->reflFields[$field] = new ReflectionEmbeddedProperty( + $parentReflFields[$mapping['declaredField']], + $reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']), + $mapping['originalClass'] + ); + continue; + } + + $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 \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service. + * + * @return void + */ + public function initializeReflection($reflService) + { + $this->reflClass = $reflService->getClass($this->name); + $this->namespace = $reflService->getClassNamespace($this->name); + + if ($this->reflClass) { + $this->name = $this->rootEntityName = $this->reflClass->getName(); + } + + $this->table['name'] = $this->namingStrategy->classToTableName($this->name); + } + + /** + * Validates Identifier. + * + * @return void + * + * @throws MappingException + */ + public function validateIdentifier() + { + if ($this->isMappedSuperclass || $this->isEmbeddedClass) { + return; + } + + // Verify & complete identifier mapping + if ( ! $this->identifier) { + throw MappingException::identifierRequired($this->name); + } + + if ($this->usesIdGenerator() && $this->isIdentifierComposite) { + throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); + } + } + + /** + * Validates association targets actually exist. + * + * @return void + * + * @throws MappingException + */ + public function validateAssociations() + { + foreach ($this->associationMappings as $mapping) { + if ( ! ClassLoader::classExists($mapping['targetEntity']) ) { + throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']); + } + } + } + + /** + * Validates lifecycle callbacks. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * + * @return void + * + * @throws MappingException + */ + public function validateLifecycleCallbacks($reflService) + { + foreach ($this->lifecycleCallbacks as $callbacks) { + foreach ($callbacks as $callbackFuncName) { + if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) { + throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName); + } + } + } + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + return $this->reflClass; + } + + /** + * @param array $cache + * + * @return void + */ + public function enableCache(array $cache) + { + if ( ! isset($cache['usage'])) { + $cache['usage'] = self::CACHE_USAGE_READ_ONLY; + } + + if ( ! isset($cache['region'])) { + $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)); + } + + $this->cache = $cache; + } + + /** + * @param string $fieldName + * @param array $cache + * + * @return void + */ + public function enableAssociationCache($fieldName, array $cache) + { + if ( ! isset($cache['usage'])) { + $cache['usage'] = isset($this->cache['usage']) + ? $this->cache['usage'] + : self::CACHE_USAGE_READ_ONLY; + } + + if ( ! isset($cache['region'])) { + $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName; + } + + $this->associationMappings[$fieldName]['cache'] = $cache; + } + + /** + * Sets the change tracking policy used by this class. + * + * @param integer $policy + * + * @return void + */ + 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->identifier) { + return false; + } + + if ( ! $this->isIdentifierComposite) { + return $fieldName === $this->identifier[0]; + } + + return in_array($fieldName, $this->identifier); + } + + /** + * Checks 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; + } + + /** + * Checks 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. + * + * @throws MappingException + */ + 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. + * + * @throws MappingException + */ + 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 The column name. + * + * @return string The column alias. + */ + public function getFieldName($columnName) + { + return isset($this->fieldNames[$columnName]) ? + $this->fieldNames[$columnName] : $columnName; + } + + /** + * Gets the named query. + * + * @see ClassMetadataInfo::$namedQueries + * + * @param string $queryName The query name. + * + * @return string + * + * @throws MappingException + */ + 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; + } + + /** + * Gets the named native query. + * + * @see ClassMetadataInfo::$namedNativeQueries + * + * @param string $queryName The query name. + * + * @return array + * + * @throws MappingException + */ + public function getNamedNativeQuery($queryName) + { + if ( ! isset($this->namedNativeQueries[$queryName])) { + throw MappingException::queryNotFound($this->name, $queryName); + } + + return $this->namedNativeQueries[$queryName]; + } + + /** + * Gets all named native queries of the class. + * + * @return array + */ + public function getNamedNativeQueries() + { + return $this->namedNativeQueries; + } + + /** + * Gets the result set mapping. + * + * @see ClassMetadataInfo::$sqlResultSetMappings + * + * @param string $name The result set mapping name. + * + * @return array + * + * @throws MappingException + */ + public function getSqlResultSetMapping($name) + { + if ( ! isset($this->sqlResultSetMappings[$name])) { + throw MappingException::resultMappingNotFound($this->name, $name); + } + + return $this->sqlResultSetMappings[$name]; + } + + /** + * Gets all sql result set mappings of the class. + * + * @return array + */ + public function getSqlResultSetMappings() + { + return $this->sqlResultSetMappings; + } + + /** + * Validates & completes the given field mapping. + * + * @param array $mapping The field mapping to validate & complete. + * + * @return array The validated and completed field mapping. + * + * @throws MappingException + */ + 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'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name); + } + + 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'])) { + $mapping['targetEntity'] = $this->fullyQualifiedClassName($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 (isset($mapping['joinColumns']) && 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::illegalToManyIdentifierAssociation($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'); + } + + if (count($cascades) !== count(array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach')))) { + throw MappingException::invalidCascadeOption( + array_diff($cascades, array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach'))), + $this->name, + $mapping['fieldName'] + ); + } + + $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. + * + * @throws RuntimeException + * @throws MappingException + */ + 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' => $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName() + )); + } + + $uniqueConstraintColumns = array(); + foreach ($mapping['joinColumns'] as &$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 { + $uniqueConstraintColumns[] = $joinColumn['name']; + } + } + + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name); + } + + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($joinColumn['name'][0] === '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } + + if ($joinColumn['referencedColumnName'][0] === '`') { + $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); + $joinColumn['quoted'] = true; + } + + $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName']) + ? $joinColumn['fieldName'] : $joinColumn['name']; + } + + if ($uniqueConstraintColumns) { + 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' => $uniqueConstraintColumns + ); + } + + $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; + + if ($mapping['orphanRemoval']) { + unset($mapping['unique']); + } + + if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) { + throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']); + } + + return $mapping; + } + + /** + * Validates & completes a one-to-many association mapping. + * + * @param array $mapping The mapping to validate and complete. + * + * @return array The validated and completed mapping. + * + * @throws MappingException + * @throws InvalidArgumentException + */ + 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; + } + + /** + * Validates & completes a many-to-many association mapping. + * + * @param array $mapping The mapping to validate & complete. + * + * @return array The validated & completed mapping. + * + * @throws \InvalidArgumentException + */ + protected function _validateAndCompleteManyToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + if ($mapping['isOwningSide']) { + // owning side MUST have a join table + if ( ! isset($mapping['joinTable']['name'])) { + $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']); + } + + $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity'] + && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns']))); + + if ( ! isset($mapping['joinTable']['joinColumns'])) { + $mapping['joinTable']['joinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE')); + } + if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + $mapping['joinTable']['inverseJoinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE')); + } + + $mapping['joinTableColumns'] = array(); + + foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']); + } + + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($joinColumn['name'][0] === '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } + + if ($joinColumn['referencedColumnName'][0] === '`') { + $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); + $joinColumn['quoted'] = true; + } + + 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'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']); + } + + if (empty($inverseJoinColumn['referencedColumnName'])) { + $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($inverseJoinColumn['name'][0] === '`') { + $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`'); + $inverseJoinColumn['quoted'] = true; + } + + if ($inverseJoinColumn['referencedColumnName'][0] === '`') { + $inverseJoinColumn['referencedColumnName'] = trim($inverseJoinColumn['referencedColumnName'], '`'); + $inverseJoinColumn['quoted'] = true; + } + + 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; + } + + /** + * {@inheritDoc} + */ + 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 + * + * @return void + */ + public function setIdentifier(array $identifier) + { + $this->identifier = $identifier; + $this->isIdentifierComposite = (count($this->identifier) > 1); + } + + /** + * {@inheritDoc} + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * {@inheritDoc} + */ + public function hasField($fieldName) + { + return isset($this->fieldMappings[$fieldName]); + } + + /** + * Gets an array containing all the column names. + * + * @param array|null $fieldNames + * + * @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. + * + * @param int $generatorType + * + * @return void + */ + 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() + { + return $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; + } + + /** + * Checks whether the class use a UUID for id generation. + * + * @return boolean + */ + public function isIdentifierUuid() + { + return $this->generatorType == self::GENERATOR_TYPE_UUID; + } + + /** + * Gets the type of a field. + * + * @param string $fieldName + * + * @return \Doctrine\DBAL\Types\Type|string|null + */ + public function getTypeOfField($fieldName) + { + return isset($this->fieldMappings[$fieldName]) ? + $this->fieldMappings[$fieldName]['type'] : null; + } + + /** + * Gets the type of a column. + * + * @param string $columnName + * + * @return \Doctrine\DBAL\Types\Type|string|null + * + * @deprecated this method is bogous and unreliable, since it cannot resolve the type of a column that is + * derived by a referenced field on a different entity. + */ + 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 primary table's schema name. + * + * @return string|null + */ + public function getSchemaName() + { + return isset($this->table['schema']) ? $this->table['schema'] : null; + } + + /** + * 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. + * + * @return void + */ + public function setSubclasses(array $subclasses) + { + foreach ($subclasses as $subclass) { + $this->subClasses[] = $this->fullyQualifiedClassName($subclass); + } + } + + /** + * Sets the parent class names. + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + * + * @param array $classNames + * + * @return void + */ + 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 its subclasses. + * + * @param integer $type + * + * @return void + * + * @throws MappingException + */ + public function setInheritanceType($type) + { + if ( ! $this->_isInheritanceType($type)) { + throw MappingException::invalidInheritanceType($this->name, $type); + } + $this->inheritanceType = $type; + } + + /** + * Sets the association to override association mapping of property for an entity relationship. + * + * @param string $fieldName + * @param array $overrideMapping + * + * @return void + * + * @throws MappingException + */ + public function setAssociationOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->associationMappings[$fieldName]; + + if (isset($overrideMapping['joinColumns'])) { + $mapping['joinColumns'] = $overrideMapping['joinColumns']; + } + + if (isset($overrideMapping['joinTable'])) { + $mapping['joinTable'] = $overrideMapping['joinTable']; + } + + $mapping['joinColumnFieldNames'] = null; + $mapping['joinTableColumns'] = null; + $mapping['sourceToTargetKeyColumns'] = null; + $mapping['relationToSourceKeyColumns'] = null; + $mapping['relationToTargetKeyColumns'] = null; + + switch ($mapping['type']) { + case self::ONE_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::ONE_TO_MANY: + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + break; + case self::MANY_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::MANY_TO_MANY: + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + break; + } + + $this->associationMappings[$fieldName] = $mapping; + } + + /** + * Sets the override for a mapped field. + * + * @param string $fieldName + * @param array $overrideMapping + * + * @return void + * + * @throws MappingException + */ + public function setAttributeOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->fieldMappings[$fieldName]; + + if (isset($mapping['id'])) { + $overrideMapping['id'] = $mapping['id']; + } + + if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) { + $overrideMapping['type'] = $mapping['type']; + } + + if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) { + $overrideMapping['fieldName'] = $mapping['fieldName']; + } + + if ($overrideMapping['type'] !== $mapping['type']) { + throw MappingException::invalidOverrideFieldType($this->name, $fieldName); + } + + unset($this->fieldMappings[$fieldName]); + unset($this->fieldNames[$mapping['columnName']]); + unset($this->columnNames[$mapping['fieldName']]); + $this->_validateAndCompleteFieldMapping($overrideMapping); + + $this->fieldMappings[$fieldName] = $overrideMapping; + } + + /** + * Checks whether a mapped field is inherited from an entity superclass. + * + * @param string $fieldName + * + * @return bool TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedField($fieldName) + { + return isset($this->fieldMappings[$fieldName]['inherited']); + } + + /** + * Checks if this entity is the root in any entity-inheritance-hierarchy. + * + * @return bool + */ + public function isRootEntity() + { + return $this->name == $this->rootEntityName; + } + + /** + * 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']); + } + + public function isInheritedEmbeddedClass($fieldName) + { + return isset($this->embeddedClasses[$fieldName]['inherited']); + } + + /** + * Sets the name of the primary table the class is mapped to. + * + * @param string $tableName The table name. + * + * @return void + * + * @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. + * + * @return void + */ + public function setPrimaryTable(array $table) + { + if (isset($table['name'])) { + // Split schema and table name from a table name like "myschema.mytable" + if (strpos($table['name'], '.') !== false) { + list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2); + } + + if ($table['name'][0] === '`') { + $table['name'] = trim($table['name'], '`'); + $this->table['quoted'] = true; + } + + $this->table['name'] = $table['name']; + } + + if (isset($table['schema'])) { + $this->table['schema'] = $table['schema']; + } + + if (isset($table['indexes'])) { + $this->table['indexes'] = $table['indexes']; + } + + if (isset($table['uniqueConstraints'])) { + $this->table['uniqueConstraints'] = $table['uniqueConstraints']; + } + + if (isset($table['options'])) { + $this->table['options'] = $table['options']; + } + } + + /** + * 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. + * + * @return void + * + * @throws MappingException + */ + public function mapField(array $mapping) + { + $this->_validateAndCompleteFieldMapping($mapping); + $this->assertFieldNotMapped($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 + * + * @return void + * + * @throws MappingException + */ + 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 $fieldMapping + * + * @return void + */ + 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. + * + * @param array $queryMapping + * + * @return void + * + * @throws MappingException + */ + public function addNamedQuery(array $queryMapping) + { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + + if (isset($this->namedQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($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 + ); + } + + /** + * INTERNAL: + * Adds a named native query to this class. + * + * @param array $queryMapping + * + * @return void + * + * @throws MappingException + */ + public function addNamedNativeQuery(array $queryMapping) + { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + + if (isset($this->namedNativeQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) { + throw MappingException::missingQueryMapping($this->name, $queryMapping['name']); + } + + $queryMapping['isSelfClass'] = false; + if (isset($queryMapping['resultClass'])) { + + if ($queryMapping['resultClass'] === '__CLASS__') { + + $queryMapping['isSelfClass'] = true; + $queryMapping['resultClass'] = $this->name; + } + + $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']); + $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\'); + } + + $this->namedNativeQueries[$queryMapping['name']] = $queryMapping; + } + + /** + * INTERNAL: + * Adds a sql result set mapping to this class. + * + * @param array $resultMapping + * + * @return void + * + * @throws MappingException + */ + public function addSqlResultSetMapping(array $resultMapping) + { + if (!isset($resultMapping['name'])) { + throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name); + } + + if (isset($this->sqlResultSetMappings[$resultMapping['name']])) { + throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']); + } + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityResult) { + if (!isset($entityResult['entityClass'])) { + throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']); + } + + $entityResult['isSelfClass'] = false; + if ($entityResult['entityClass'] === '__CLASS__') { + + $entityResult['isSelfClass'] = true; + $entityResult['entityClass'] = $this->name; + + } + + $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']); + + $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\'); + $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass']; + + if (isset($entityResult['fields'])) { + foreach ($entityResult['fields'] as $k => $field) { + if (!isset($field['name'])) { + throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']); + } + + if (!isset($field['column'])) { + $fieldName = $field['name']; + if (strpos($fieldName, '.')) { + list(, $fieldName) = explode('.', $fieldName); + } + + $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName; + } + } + } + } + } + + $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping; + } + + /** + * Adds a one-to-one mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + 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. + * + * @return void + */ + 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. + * + * @return void + */ + 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. + * + * @return void + */ + 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 + * + * @return void + * + * @throws MappingException + */ + protected function _storeAssociationMapping(array $assocMapping) + { + $sourceFieldName = $assocMapping['fieldName']; + + $this->assertFieldNotMapped($sourceFieldName); + + $this->associationMappings[$sourceFieldName] = $assocMapping; + } + + /** + * Registers a custom repository class for the entity class. + * + * @param string $repositoryClassName The class name of the custom mapper. + * + * @return void + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName); + } + + /** + * Dispatches the lifecycle event of the given entity to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker + * + * @param string $lifecycleEvent The lifecycle event. + * @param object $entity The Entity on which the event occurred. + * + * @return void + */ + 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 + * + * @return void + */ + public function addLifecycleCallback($callback, $event) + { + if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) { + return; + } + + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the lifecycle callbacks for entities of this class. + * Any previously registered callbacks are overwritten. + * + * @param array $callbacks + * + * @return void + */ + public function setLifecycleCallbacks(array $callbacks) + { + $this->lifecycleCallbacks = $callbacks; + } + + /** + * Adds a entity listener for entities of this class. + * + * @param string $eventName The entity lifecycle event. + * @param string $class The listener class. + * @param string $method The listener callback method. + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + public function addEntityListener($eventName, $class, $method) + { + $class = $this->fullyQualifiedClassName($class); + $listener = array( + 'class' => $class, + 'method' => $method + ); + + if ( ! class_exists($class)) { + throw MappingException::entityListenerClassNotFound($class, $this->name); + } + + if ( ! method_exists($class, $method)) { + throw MappingException::entityListenerMethodNotFound($class, $method, $this->name); + } + + if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) { + throw MappingException::duplicateEntityListener($class, $method, $this->name); + } + + $this->entityListeners[$eventName][] = $listener; + } + + /** + * Sets the discriminator column definition. + * + * @param array $columnDef + * + * @return void + * + * @throws MappingException + * + * @see getDiscriminatorColumn() + */ + public function setDiscriminatorColumn($columnDef) + { + if ($columnDef !== null) { + if ( ! isset($columnDef['name'])) { + throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); + } + + if (isset($this->fieldNames[$columnDef['name']])) { + throw MappingException::duplicateColumnName($this->name, $columnDef['name']); + } + + 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 + * + * @return void + */ + public function setDiscriminatorMap(array $map) + { + foreach ($map as $value => $className) { + $this->addDiscriminatorMapClass($value, $className); + } + } + + /** + * Adds one entry of the discriminator map with a new class and corresponding name. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws MappingException + */ + public function addDiscriminatorMapClass($name, $className) + { + $className = $this->fullyQualifiedClassName($className); + $className = ltrim($className, '\\'); + $this->discriminatorMap[$name] = $className; + + if ($this->name === $className) { + $this->discriminatorValue = $name; + + return; + } + + if ( ! (class_exists($className) || interface_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 $queryName + * + * @return boolean + */ + public function hasNamedQuery($queryName) + { + return isset($this->namedQueries[$queryName]); + } + + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $queryName + * + * @return boolean + */ + public function hasNamedNativeQuery($queryName) + { + return isset($this->namedNativeQueries[$queryName]); + } + + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $name + * + * @return boolean + */ + public function hasSqlResultSetMapping($name) + { + return isset($this->sqlResultSetMappings[$name]); + } + + /** + * {@inheritDoc} + */ + public function hasAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]); + } + + /** + * {@inheritDoc} + */ + public function isSingleValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); + } + + /** + * {@inheritDoc} + */ + 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]) + ); + } + + /** + * Returns the single association join column (if any). + * + * @param string $fieldName + * + * @return string + * + * @throws MappingException + */ + public function getSingleAssociationJoinColumnName($fieldName) + { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; + } + + /** + * Returns the single association referenced join column name (if any). + * + * @param string $fieldName + * + * @return string + * + * @throws MappingException + */ + 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 + * + * @throws MappingException + */ + 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 \Doctrine\ORM\Id\AbstractIdGenerator $generator + * + * @return void + */ + public function setIdGenerator($generator) + { + $this->idGenerator = $generator; + } + + /** + * Sets definition. + * + * @param array $definition + * + * @return void + */ + public function setCustomGeneratorDefinition(array $definition) + { + $this->customGeneratorDefinition = $definition; + } + + /** + * 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 + * 'quoted' => 1 + * ) + * + * + * @param array $definition + * + * @return void + */ + public function setSequenceGeneratorDefinition(array $definition) + { + if ( ! isset($definition['sequenceName'])) { + throw MappingException::missingSequenceName($this->name); + } + + if ($definition['sequenceName'][0] == '`') { + $definition['sequenceName'] = trim($definition['sequenceName'], '`'); + $definition['quoted'] = true; + } + + $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. + * + * @return void + * + * @throws MappingException + */ + 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 + * + * @return void + */ + 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 + * + * @return void + */ + public function setVersionField($versionField) + { + $this->versionField = $versionField; + } + + /** + * Marks this class as read only, no change tracking is applied to it. + * + * @return void + */ + public function markReadOnly() + { + $this->isReadOnly = true; + } + + /** + * {@inheritDoc} + */ + public function getFieldNames() + { + return array_keys($this->fieldMappings); + } + + /** + * {@inheritDoc} + */ + public function getAssociationNames() + { + return array_keys($this->associationMappings); + } + + /** + * {@inheritDoc} + * + * @throws InvalidArgumentException + */ + 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']; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param \Doctrine\DBAL\Platforms\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) use ($platform) { + 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. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param string $field + * @param \Doctrine\DBAL\Platforms\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. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param \Doctrine\DBAL\Platforms\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. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param array $assoc + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedJoinTableName(array $assoc, $platform) + { + return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name']; + } + + /** + * {@inheritDoc} + */ + public function isAssociationInverseSide($fieldName) + { + return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide']; + } + + /** + * {@inheritDoc} + */ + public function getAssociationMappedByTargetField($fieldName) + { + return $this->associationMappings[$fieldName]['mappedBy']; + } + + /** + * @param string $targetClass + * + * @return array + */ + public function getAssociationsByTargetClass($targetClass) + { + $relations = array(); + foreach ($this->associationMappings as $mapping) { + if ($mapping['targetEntity'] == $targetClass) { + $relations[$mapping['fieldName']] = $mapping; + } + } + return $relations; + } + + /** + * @param string|null $className + * @return string|null null if the input value is null + */ + public function fullyQualifiedClassName($className) + { + if (empty($className)) { + return $className; + } + + if ($className !== null && strpos($className, '\\') === false && strlen($this->namespace) > 0) { + return $this->namespace . '\\' . $className; + } + + return $className; + } + + /** + * @param string $name + * + * @return mixed + */ + public function getMetadataValue($name) { + + if (isset($this->$name)) { + return $this->$name; + } + + return null; + } + + /** + * Map Embedded Class + * + * @param array $mapping + * @throws MappingException + * @return void + */ + public function mapEmbedded(array $mapping) + { + $this->assertFieldNotMapped($mapping['fieldName']); + + $this->embeddedClasses[$mapping['fieldName']] = array( + 'class' => $this->fullyQualifiedClassName($mapping['class']), + 'columnPrefix' => $mapping['columnPrefix'], + 'declaredField' => isset($mapping['declaredField']) ? $mapping['declaredField'] : null, + 'originalField' => isset($mapping['originalField']) ? $mapping['originalField'] : null, + ); + } + + /** + * Inline the embeddable class + * + * @param string $property + * @param ClassMetadataInfo $embeddable + */ + public function inlineEmbeddable($property, ClassMetadataInfo $embeddable) + { + foreach ($embeddable->fieldMappings as $fieldMapping) { + $fieldMapping['originalClass'] = isset($fieldMapping['originalClass']) + ? $fieldMapping['originalClass'] + : $embeddable->name; + $fieldMapping['declaredField'] = isset($fieldMapping['declaredField']) + ? $property . '.' . $fieldMapping['declaredField'] + : $property; + $fieldMapping['originalField'] = isset($fieldMapping['originalField']) + ? $fieldMapping['originalField'] + : $fieldMapping['fieldName']; + $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName']; + + if (! empty($this->embeddedClasses[$property]['columnPrefix'])) { + $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName']; + } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) { + $fieldMapping['columnName'] = $this->namingStrategy + ->embeddedFieldToColumnName( + $property, + $fieldMapping['columnName'], + $this->reflClass->name, + $embeddable->reflClass->name + ); + } + + $this->mapField($fieldMapping); + } + } + + /** + * @param string $fieldName + * @throws MappingException + */ + private function assertFieldNotMapped($fieldName) + { + if (isset($this->fieldMappings[$fieldName]) || + isset($this->associationMappings[$fieldName]) || + isset($this->embeddedClasses[$fieldName])) { + + throw MappingException::duplicateFieldMapping($this->name, $fieldName); + } + } + + /** + * Gets the sequence name based on class metadata. + * + * @param AbstractPlatform $platform + * @return string + * + * @todo Sequence names should be computed in DBAL depending on the platform + */ + public function getSequenceName(AbstractPlatform $platform) + { + $sequencePrefix = $this->getSequencePrefix($platform); + + $columnName = $this->getSingleIdentifierColumnName(); + $sequenceName = $sequencePrefix . '_' . $columnName . '_seq'; + + return $sequenceName; + } + + /** + * Gets the sequence name prefix based on class metadata. + * + * @param AbstractPlatform $platform + * @return string + * + * @todo Sequence names should be computed in DBAL depending on the platform + */ + public function getSequencePrefix(AbstractPlatform $platform) + { + $tableName = $this->getTableName(); + $sequencePrefix = $tableName; + + // Prepend the schema name to the table name if there is one + if ($schemaName = $this->getSchemaName()) { + $sequencePrefix = $schemaName . '.' . $tableName; + + if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) { + $sequencePrefix = $schemaName . '__' . $tableName; + } + } + + return $sequencePrefix; + } +} 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 00000000000..70337323f6f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class Column implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var mixed + */ + public $type = 'string'; + + /** + * @var integer + */ + public $length; + + /** + * The precision for a decimal (exact numeric) column (Applies only for decimal column). + * + * @var integer + */ + public $precision = 0; + + /** + * The scale for a decimal (exact numeric) column (Applies only for decimal column). + * + * @var integer + */ + public $scale = 0; + + /** + * @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/ColumnResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php new file mode 100644 index 00000000000..a164c85c0ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References name of a column in the SELECT clause of a SQL query. + * Scalar result types can be included in the query result by specifying this annotation in the metadata. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class ColumnResult implements Annotation +{ + /** + * The name of a column in the SELECT clause of a SQL query. + * + * @var string + */ + public $name; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php new file mode 100644 index 00000000000..41e200e12a7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class CustomIdGenerator implements Annotation +{ + /** + * @var string + */ + public $class; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php new file mode 100644 index 00000000000..75658547e17 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The default DefaultEntityListener + * + * @since 2.4 + * @author Fabio B. Silva + */ +class DefaultEntityListenerResolver implements EntityListenerResolver +{ + /** + * @var array Map to store entity listener instances. + */ + private $instances = array(); + + /** + * {@inheritdoc} + */ + public function clear($className = null) + { + if ($className === null) { + $this->instances = array(); + + return; + } + + if (isset($this->instances[$className = trim($className, '\\')])) { + unset($this->instances[$className]); + } + } + + /** + * {@inheritdoc} + */ + public function register($object) + { + if ( ! is_object($object)) { + throw new \InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object))); + } + + $this->instances[get_class($object)] = $object; + } + + /** + * {@inheritdoc} + */ + public function resolve($className) + { + if (isset($this->instances[$className = trim($className, '\\')])) { + return $this->instances[$className]; + } + + return $this->instances[$className] = new $className(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php new file mode 100644 index 00000000000..1e75f979451 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The default NamingStrategy + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +class DefaultNamingStrategy implements NamingStrategy +{ + /** + * {@inheritdoc} + */ + public function classToTableName($className) + { + if (strpos($className, '\\') !== false) { + return substr($className, strrpos($className, '\\') + 1); + } + + return $className; + } + + /** + * {@inheritdoc} + */ + public function propertyToColumnName($propertyName, $className = null) + { + return $propertyName; + } + + /** + * {@inheritdoc} + */ + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) + { + return $propertyName.'_'.$embeddedColumnName; + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName, $className = null) + { + return $propertyName . '_' . $this->referenceColumnName(); + } + + /** + * {@inheritdoc} + */ + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return strtolower($this->classToTableName($sourceEntity) . '_' . + $this->classToTableName($targetEntity)); + } + + /** + * {@inheritdoc} + */ + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return strtolower($this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName())); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php new file mode 100644 index 00000000000..dfbded8a4a9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * A set of rules for determining the physical column, alias and table quotes + * + * @since 2.3 + * @author Fabio B. Silva + */ +class DefaultQuoteStrategy implements QuoteStrategy +{ + /** + * {@inheritdoc} + */ + public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($class->fieldMappings[$fieldName]['quoted']) + ? $platform->quoteIdentifier($class->fieldMappings[$fieldName]['columnName']) + : $class->fieldMappings[$fieldName]['columnName']; + } + + /** + * {@inheritdoc} + * + * @todo Table names should be computed in DBAL depending on the platform + */ + public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + { + $tableName = $class->table['name']; + + if ( ! empty($class->table['schema'])) { + $tableName = $class->table['schema'] . '.' . $class->table['name']; + + if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) { + $tableName = $class->table['schema'] . '__' . $class->table['name']; + } + } + + return isset($class->table['quoted']) + ? $platform->quoteIdentifier($tableName) + : $tableName; + } + + /** + * {@inheritdoc} + */ + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($definition['quoted']) + ? $platform->quoteIdentifier($definition['sequenceName']) + : $definition['sequenceName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + } + + /** + * {@inheritdoc} + */ + public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['referencedColumnName']) + : $joinColumn['referencedColumnName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) + { + $schema = ''; + + if (isset($association['joinTable']['schema'])) { + $schema = $association['joinTable']['schema'] . '.'; + } + + $tableName = $association['joinTable']['name']; + + if (isset($association['joinTable']['quoted'])) { + $tableName = $platform->quoteIdentifier($tableName); + } + + return $schema . $tableName; + } + + /** + * {@inheritdoc} + */ + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + { + $quotedColumnNames = array(); + + foreach ($class->identifier as $fieldName) { + if (isset($class->fieldMappings[$fieldName])) { + $quotedColumnNames[] = $this->getColumnName($fieldName, $class, $platform); + + continue; + } + + // Association defined as Id field + $joinColumns = $class->associationMappings[$fieldName]['joinColumns']; + $assocQuotedColumnNames = array_map( + function ($joinColumn) use ($platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + }, + $joinColumns + ); + + $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); + } + + return $quotedColumnNames; + } + + /** + * {@inheritdoc} + */ + public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) + { + // 1 ) Concatenate column name and counter + // 2 ) 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. + // 3 ) Strip non alphanumeric characters + // 4 ) Prefix with "_" if the result its numeric + $columnName = $columnName . '_' . $counter; + $columnName = substr($columnName, -$platform->getMaxIdentifierLength()); + $columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName); + $columnName = is_numeric($columnName) ? '_' . $columnName : $columnName; + + return $platform->getSQLResultCasing($columnName); + } +} 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 00000000000..97ca7e9b577 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorColumn implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $type; + + /** + * @var integer + */ + public $length; + + /** + * Field name used in non-object hydration (array/scalar). + * + * @var mixed + */ + public $fieldName; + + /** + * @var string + */ + public $columnDefinition; +} 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 00000000000..09d619465cd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php @@ -0,0 +1,32 @@ +. + */ + +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/AnnotationDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 00000000000..df8b7b80c49 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,638 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\JoinColumn; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; +use Doctrine\ORM\Events; + +/** + * 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 extends AbstractAnnotationDriver +{ + /** + * {@inheritDoc} + */ + protected $entityAnnotationClasses = array( + 'Doctrine\ORM\Mapping\Entity' => 1, + 'Doctrine\ORM\Mapping\MappedSuperclass' => 2, + ); + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + $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) { + foreach ($classAnnotations as $key => $annot) { + if ( ! is_numeric($key)) { + continue; + } + + $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 if (isset($classAnnotations['Doctrine\ORM\Mapping\Embeddable'])) { + $metadata->isEmbeddedClass = 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->flags)) { + $index['flags'] = $indexAnnot->flags; + } + + if ( ! empty($indexAnnot->options)) { + $index['options'] = $indexAnnot->options; + } + + 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->options)) { + $uniqueConstraint['options'] = $uniqueConstraintAnnot->options; + } + + if ( ! empty($uniqueConstraintAnnot->name)) { + $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint; + } else { + $primaryTable['uniqueConstraints'][] = $uniqueConstraint; + } + } + } + + if ($tableAnnot->options) { + $primaryTable['options'] = $tableAnnot->options; + } + + $metadata->setPrimaryTable($primaryTable); + } + + // Evaluate @Cache annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Cache'])) { + $cacheAnnot = $classAnnotations['Doctrine\ORM\Mapping\Cache']; + $cacheMap = array( + 'region' => $cacheAnnot->region, + 'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), + ); + + $metadata->enableCache($cacheMap); + } + + // Evaluate NamedNativeQueries annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'])) { + $namedNativeQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries']; + + foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) { + $metadata->addNamedNativeQuery(array( + 'name' => $namedNativeQuery->name, + 'query' => $namedNativeQuery->query, + 'resultClass' => $namedNativeQuery->resultClass, + 'resultSetMapping' => $namedNativeQuery->resultSetMapping, + )); + } + } + + // Evaluate SqlResultSetMappings annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'])) { + $sqlResultSetMappingsAnnot = $classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings']; + + foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) { + $entities = array(); + $columns = array(); + foreach ($resultSetMapping->entities as $entityResultAnnot) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => $entityResultAnnot->entityClass, + 'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, + ); + + foreach ($entityResultAnnot->fields as $fieldResultAnnot) { + $entityResult['fields'][] = array( + 'name' => $fieldResultAnnot->name, + 'column' => $fieldResultAnnot->column + ); + } + + $entities[] = $entityResult; + } + + foreach ($resultSetMapping->columns as $columnResultAnnot) { + $columns[] = array( + 'name' => $columnResultAnnot->name, + ); + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping->name, + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + // 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, + 'columnDefinition' => $discrColumnAnnot->columnDefinition + )); + } 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 + /* @var $property \ReflectionProperty */ + foreach ($class->getProperties() as $property) { + if ($metadata->isMappedSuperclass && ! $property->isPrivate() + || + $metadata->isInheritedField($property->name) + || + $metadata->isInheritedAssociation($property->name) + || + $metadata->isInheritedEmbeddedClass($property->name)) { + continue; + } + + $mapping = array(); + $mapping['fieldName'] = $property->getName(); + + // Check for JoinColumn/JoinColumns annotations + $joinColumns = array(); + + if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) { + $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot); + } else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) { + foreach ($joinColumnsAnnot->value as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + } + + // 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 = $this->columnToArray($property->getName(), $columnAnnot); + + 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 ($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 ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\CustomIdGenerator')) { + $metadata->setCustomGeneratorDefinition(array( + 'class' => $customGeneratorAnnot->class + )); + } + } 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'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + } + + $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); + } else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Embedded')) { + $mapping['class'] = $embeddedAnnot->class; + $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix; + $metadata->mapEmbedded($mapping); + } + + // Evaluate @Cache annotation + if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Cache')) !== null) { + $metadata->enableAssociationCache($mapping['fieldName'], array( + 'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), + 'region' => $cacheAnnot->region, + )); + } + } + + // Evaluate AssociationOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'])) { + $associationOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides']; + + foreach ($associationOverridesAnnot->value as $associationOverride) { + $override = array(); + $fieldName = $associationOverride->name; + + // Check for JoinColumn/JoinColumns annotations + if ($associationOverride->joinColumns) { + $joinColumns = array(); + foreach ($associationOverride->joinColumns as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for JoinTable annotations + if ($associationOverride->joinTable) { + $joinTableAnnot = $associationOverride->joinTable; + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate AttributeOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'])) { + $attributeOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides']; + foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) { + $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column); + $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride); + } + } + + // Evaluate EntityListeners annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\EntityListeners'])) { + $entityListenersAnnot = $classAnnotations['Doctrine\ORM\Mapping\EntityListeners']; + + foreach ($entityListenersAnnot->value as $item) { + $listenerClassName = $metadata->fullyQualifiedClassName($item); + + if ( ! class_exists($listenerClassName)) { + throw MappingException::entityListenerClassNotFound($listenerClassName, $className); + } + + $hasMapping = false; + $listenerClass = new \ReflectionClass($listenerClassName); + /* @var $method \ReflectionMethod */ + foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + // find method callbacks. + $callbacks = $this->getMethodCallbacks($method); + $hasMapping = $hasMapping ?: ( ! empty($callbacks)); + + foreach ($callbacks as $value) { + $metadata->addEntityListener($value[1], $listenerClassName, $value[0]); + } + } + // Evaluate the listener using naming convention. + if ( ! $hasMapping ) { + EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName); + } + } + } + + // Evaluate @HasLifecycleCallbacks annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) { + /* @var $method \ReflectionMethod */ + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + + foreach ($this->getMethodCallbacks($method) as $value) { + + $metadata->addLifecycleCallback($value[0], $value[1]); + } + } + } + } + + /** + * 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); + } + + /** + * Parses the given method. + * + * @param \ReflectionMethod $method + * + * @return array + */ + private function getMethodCallbacks(\ReflectionMethod $method) + { + $callbacks = array(); + $annotations = $this->reader->getMethodAnnotations($method); + + foreach ($annotations as $annot) { + if ($annot instanceof \Doctrine\ORM\Mapping\PrePersist) { + $callbacks[] = array($method->name, Events::prePersist); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostPersist) { + $callbacks[] = array($method->name, Events::postPersist); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreUpdate) { + $callbacks[] = array($method->name, Events::preUpdate); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostUpdate) { + $callbacks[] = array($method->name, Events::postUpdate); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreRemove) { + $callbacks[] = array($method->name, Events::preRemove); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostRemove) { + $callbacks[] = array($method->name, Events::postRemove); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostLoad) { + $callbacks[] = array($method->name, Events::postLoad); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreFlush) { + $callbacks[] = array($method->name, Events::preFlush); + } + } + + return $callbacks; + } + + /** + * Parse the given JoinColumn as array + * + * @param JoinColumn $joinColumn + * @return array + */ + private function joinColumnToArray(JoinColumn $joinColumn) + { + return array( + 'name' => $joinColumn->name, + 'unique' => $joinColumn->unique, + 'nullable' => $joinColumn->nullable, + 'onDelete' => $joinColumn->onDelete, + 'columnDefinition' => $joinColumn->columnDefinition, + 'referencedColumnName' => $joinColumn->referencedColumnName, + ); + } + + /** + * Parse the given Column as array + * + * @param string $fieldName + * @param Column $column + * + * @return array + */ + private function columnToArray($fieldName, Column $column) + { + $mapping = array( + 'fieldName' => $fieldName, + 'type' => $column->type, + 'scale' => $column->scale, + 'length' => $column->length, + 'unique' => $column->unique, + 'nullable' => $column->nullable, + 'precision' => $column->precision + ); + + if ($column->options) { + $mapping['options'] = $column->options; + } + + if (isset($column->name)) { + $mapping['columnName'] = $column->name; + } + + if (isset($column->columnDefinition)) { + $mapping['columnDefinition'] = $column->columnDefinition; + } + + return $mapping; + } + + /** + * Factory method for the Annotation Driver. + * + * @param array|string $paths + * @param AnnotationReader|null $reader + * + * @return AnnotationDriver + */ + static public function create($paths = array(), AnnotationReader $reader = null) + { + if ($reader == null) { + $reader = new AnnotationReader(); + } + + 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 00000000000..e51c15282e8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -0,0 +1,555 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\MappingException; + +/** + * The DatabaseDriver reverse engineers the mapping metadata from a database. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Benjamin Eberlei + */ +class DatabaseDriver implements MappingDriver +{ + /** + * @var AbstractSchemaManager + */ + private $_sm; + + /** + * @var array|null + */ + private $tables = null; + + /** + * @var array + */ + 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|null + */ + private $namespace; + + /** + * @param AbstractSchemaManager $schemaManager + */ + public function __construct(AbstractSchemaManager $schemaManager) + { + $this->_sm = $schemaManager; + } + + /** + * Set the namespace for the generated entities. + * + * @param string $namespace + * + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $this->reverseEngineerMappingFromDatabase(); + + return array_keys($this->classToTableNames); + } + + /** + * Sets class name for a table. + * + * @param string $tableName + * @param string $className + * + * @return void + */ + public function setClassNameForTable($tableName, $className) + { + $this->classNamesForTables[$tableName] = $className; + } + + /** + * Sets 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; + } + + /** + * Sets tables manually instead of relying on the reverse engineering 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; + } + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $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; + + $this->buildIndexes($metadata); + $this->buildFieldMappings($metadata); + $this->buildToOneAssociationMappings($metadata); + + 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()))) { + continue; + } + + $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 engineering. + 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; + } + } + } + + /** + * @return void + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + 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) { + $foreignKeys = ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) + ? $table->getForeignKeys() + : 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; + } + } + } + + /** + * Build indexes from a class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildIndexes(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $indexes = $this->tables[$tableName]->getIndexes(); + + foreach($indexes as $index){ + if ($index->isPrimary()) { + continue; + } + + $indexName = $index->getName(); + $indexColumns = $index->getColumns(); + $constraintType = $index->isUnique() + ? 'uniqueConstraints' + : 'indexes'; + + $metadata->table[$constraintType][$indexName]['columns'] = $indexColumns; + } + } + + /** + * Build field mapping from class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildFieldMappings(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $columns = $this->tables[$tableName]->getColumns(); + $primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]); + $foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]); + $allForeignKeys = array(); + + foreach ($foreignKeys as $foreignKey) { + $allForeignKeys = array_merge($allForeignKeys, $foreignKey->getLocalColumns()); + } + + $ids = array(); + $fieldMappings = array(); + + foreach ($columns as $column) { + if (in_array($column->getName(), $allForeignKeys)) { + continue; + } + + $fieldMapping = $this->buildFieldMapping($tableName, $column); + + if ($primaryKeys && in_array($column->getName(), $primaryKeys)) { + $fieldMapping['id'] = true; + $ids[] = $fieldMapping; + } + + $fieldMappings[] = $fieldMapping; + } + + // We need to check for the columns here, because we might have associations as id as well. + if ($ids && count($primaryKeys) == 1) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + + foreach ($fieldMappings as $fieldMapping) { + $metadata->mapField($fieldMapping); + } + } + + /** + * Build field mapping from a schema column definition + * + * @param string $tableName + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return array + */ + private function buildFieldMapping($tableName, Column $column) + { + $fieldMapping = array( + 'fieldName' => $this->getFieldNameForColumn($tableName, $column->getName(), false), + 'columnName' => $column->getName(), + 'type' => $column->getType()->getName(), + 'nullable' => ( ! $column->getNotNull()), + ); + + // Type specific elements + switch ($fieldMapping['type']) { + case Type::TARRAY: + case Type::BLOB: + case Type::GUID: + case Type::JSON_ARRAY: + case Type::OBJECT: + case Type::SIMPLE_ARRAY: + case Type::STRING: + case Type::TEXT: + $fieldMapping['length'] = $column->getLength(); + $fieldMapping['options']['fixed'] = $column->getFixed(); + break; + + case Type::DECIMAL: + case Type::FLOAT: + $fieldMapping['precision'] = $column->getPrecision(); + $fieldMapping['scale'] = $column->getScale(); + break; + + case Type::INTEGER: + case Type::BIGINT: + case Type::SMALLINT: + $fieldMapping['options']['unsigned'] = $column->getUnsigned(); + break; + } + + // Comment + if (($comment = $column->getComment()) !== null) { + $fieldMapping['options']['comment'] = $comment; + } + + // Default + if (($default = $column->getDefault()) !== null) { + $fieldMapping['options']['default'] = $default; + } + + return $fieldMapping; + } + + /** + * Build to one (one to one, many to one) association mapping from class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildToOneAssociationMappings(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]); + $foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]); + + foreach ($foreignKeys as $foreignKey) { + $foreignTableName = $foreignKey->getForeignTableName(); + $fkColumns = $foreignKey->getColumns(); + $fkForeignColumns = $foreignKey->getForeignColumns(); + $localColumn = current($fkColumns); + $associationMapping = array( + 'fieldName' => $this->getFieldNameForColumn($tableName, $localColumn, true), + 'targetEntity' => $this->getClassNameForTable($foreignTableName), + ); + + if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) { + $associationMapping['fieldName'] .= '2'; // "foo" => "foo2" + } + + if ($primaryKeys && in_array($localColumn, $primaryKeys)) { + $associationMapping['id'] = true; + } + + for ($i = 0; $i < count($fkColumns); $i++) { + $associationMapping['joinColumns'][] = array( + 'name' => $fkColumns[$i], + 'referencedColumnName' => $fkForeignColumns[$i], + ); + } + + // Here we need to check if $fkColumns are the same as $primaryKeys + if ( ! array_diff($fkColumns, $primaryKeys)) { + $metadata->mapOneToOne($associationMapping); + } else { + $metadata->mapManyToOne($associationMapping); + } + } + } + + /** + * Retreive schema table definition foreign keys. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return array + */ + private function getTableForeignKeys(Table $table) + { + return ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) + ? $table->getForeignKeys() + : array(); + } + + /** + * Retreive schema table definition primary keys. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return array + */ + private function getTablePrimaryKeys(Table $table) + { + try { + return $table->getPrimaryKey()->getColumns(); + } catch(SchemaException $e) { + // Do nothing + } + + return array(); + } + + /** + * Returns 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); + } +} 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 00000000000..8f4a34c0ef4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -0,0 +1,69 @@ +. + */ + +require_once __DIR__.'/../Annotation.php'; +require_once __DIR__.'/../Entity.php'; +require_once __DIR__.'/../Embeddable.php'; +require_once __DIR__.'/../Embedded.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__.'/../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__.'/../CustomIdGenerator.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'; +require_once __DIR__.'/../FieldResult.php'; +require_once __DIR__.'/../ColumnResult.php'; +require_once __DIR__.'/../EntityResult.php'; +require_once __DIR__.'/../NamedNativeQuery.php'; +require_once __DIR__.'/../NamedNativeQueries.php'; +require_once __DIR__.'/../SqlResultSetMapping.php'; +require_once __DIR__.'/../SqlResultSetMappings.php'; +require_once __DIR__.'/../AssociationOverride.php'; +require_once __DIR__.'/../AssociationOverrides.php'; +require_once __DIR__.'/../AttributeOverride.php'; +require_once __DIR__.'/../AttributeOverrides.php'; +require_once __DIR__.'/../EntityListeners.php'; +require_once __DIR__.'/../Cache.php'; 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 00000000000..f4cd8cd6081 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain instead + */ +class DriverChain extends MappingDriverChain +{ +} 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 00000000000..28e2dbea22f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver as CommonPHPDriver; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver instead + */ +class PHPDriver extends CommonPHPDriver +{ +} 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 00000000000..85221f3a2bc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php @@ -0,0 +1,43 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator; + +/** + * XmlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedXmlDriver extends XmlDriver +{ + const DEFAULT_FILE_EXTENSION = '.orm.xml'; + + /** + * {@inheritDoc} + */ + public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension); + parent::__construct($locator, $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 00000000000..d2b8c0fcf03 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php @@ -0,0 +1,43 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator; + +/** + * YamlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedYamlDriver extends YamlDriver +{ + const DEFAULT_FILE_EXTENSION = '.orm.yml'; + + /** + * {@inheritDoc} + */ + public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension); + parent::__construct($locator, $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 00000000000..d6c6ead853c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver as CommonStaticPHPDriver; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver instead + */ +class StaticPHPDriver extends CommonStaticPHPDriver +{ +} 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 00000000000..c917eb24599 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -0,0 +1,847 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use SimpleXMLElement; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\MappingException; + +/** + * XmlDriver is a metadata driver that enables mapping through XML files. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class XmlDriver extends FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.xml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + /* @var $xmlRoot SimpleXMLElement */ + $xmlRoot = $this->getElement($className); + + if ($xmlRoot->getName() == 'entity') { + if (isset($xmlRoot['repository-class'])) { + $metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']); + } + if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) { + $metadata->markReadOnly(); + } + } else if ($xmlRoot->getName() == 'mapped-superclass') { + $metadata->setCustomRepositoryClass( + isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null + ); + $metadata->isMappedSuperclass = true; + } else if ($xmlRoot->getName() == 'embeddable') { + $metadata->isEmbeddedClass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate attributes + $primaryTable = array(); + + if (isset($xmlRoot['table'])) { + $primaryTable['name'] = (string) $xmlRoot['table']; + } + + if (isset($xmlRoot['schema'])) { + $primaryTable['schema'] = (string) $xmlRoot['schema']; + } + + $metadata->setPrimaryTable($primaryTable); + + // Evaluate second level cache + if (isset($xmlRoot->cache)) { + $metadata->enableCache($this->cacheToArray($xmlRoot->cache)); + } + + // 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'] + )); + } + } + + // Evaluate native named queries + if (isset($xmlRoot->{'named-native-queries'})) { + foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) { + $metadata->addNamedNativeQuery(array( + 'name' => isset($nativeQueryElement['name']) ? (string)$nativeQueryElement['name'] : null, + 'query' => isset($nativeQueryElement->query) ? (string)$nativeQueryElement->query : null, + 'resultClass' => isset($nativeQueryElement['result-class']) ? (string)$nativeQueryElement['result-class'] : null, + 'resultSetMapping' => isset($nativeQueryElement['result-set-mapping']) ? (string)$nativeQueryElement['result-set-mapping'] : null, + )); + } + } + + // Evaluate sql result set mapping + if (isset($xmlRoot->{'sql-result-set-mappings'})) { + foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) { + $entities = array(); + $columns = array(); + foreach ($rsmElement as $entityElement) { + // + if (isset($entityElement['entity-class'])) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => (string)$entityElement['entity-class'], + 'discriminatorColumn' => isset($entityElement['discriminator-column']) ? (string)$entityElement['discriminator-column'] : null, + ); + + foreach ($entityElement as $fieldElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldElement['name']) ? (string)$fieldElement['name'] : null, + 'column' => isset($fieldElement['column']) ? (string)$fieldElement['column'] : null, + ); + } + + $entities[] = $entityResult; + } + + // + if (isset($entityElement['name'])) { + $columns[] = array( + 'name' => (string)$entityElement['name'], + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => (string)$rsmElement['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + 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' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null, + 'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null, + 'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null, + 'columnDefinition' => isset($discrColumn['column-definition']) ? (string)$discrColumn['column-definition'] : null + )); + } 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 $indexXml) { + $index = array('columns' => explode(',', (string) $indexXml['columns'])); + + if (isset($indexXml['flags'])) { + $index['flags'] = explode(',', (string) $indexXml['flags']); + } + + if (isset($indexXml->options)) { + $index['options'] = $this->_parseOptions($indexXml->options->children()); + } + + if (isset($indexXml['name'])) { + $metadata->table['indexes'][(string) $indexXml['name']] = $index; + } else { + $metadata->table['indexes'][] = $index; + } + } + } + + // Evaluate + if (isset($xmlRoot->{'unique-constraints'})) { + $metadata->table['uniqueConstraints'] = array(); + foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) { + $unique = array('columns' => explode(',', (string) $uniqueXml['columns'])); + + + if (isset($uniqueXml->options)) { + $unique['options'] = $this->_parseOptions($uniqueXml->options->children()); + } + + if (isset($uniqueXml['name'])) { + $metadata->table['uniqueConstraints'][(string)$uniqueXml['name']] = $unique; + } else { + $metadata->table['uniqueConstraints'][] = $unique; + } + } + } + + if (isset($xmlRoot->options)) { + $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children()); + } + + // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions + // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception + $mappings = array(); + // Evaluate mappings + if (isset($xmlRoot->field)) { + foreach ($xmlRoot->field as $fieldMapping) { + $mapping = $this->columnToArray($fieldMapping); + + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + unset($mapping['version']); + } + + $metadata->mapField($mapping); + } + } + + if (isset($xmlRoot->embedded)) { + foreach ($xmlRoot->embedded as $embeddedMapping) { + $columnPrefix = isset($embeddedMapping['column-prefix']) + ? (string) $embeddedMapping['column-prefix'] + : null; + + $useColumnPrefix = isset($embeddedMapping['use-column-prefix']) + ? $this->evaluateBoolean($embeddedMapping['use-column-prefix']) + : true; + + $mapping = array( + 'fieldName' => (string) $embeddedMapping['name'], + 'class' => (string) $embeddedMapping['class'], + 'columnPrefix' => $useColumnPrefix ? $columnPrefix : false + ); + + $metadata->mapEmbedded($mapping); + } + } + + foreach ($mappings as $mapping) { + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + } + + $metadata->mapField($mapping); + } + + // Evaluate mappings + $associationIds = array(); + foreach ($xmlRoot->id as $idElement) { + if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) { + $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['length'])) { + $mapping['length'] = (string)$idElement['length']; + } + + if (isset($idElement['column'])) { + $mapping['columnName'] = (string)$idElement['column']; + } + + if (isset($idElement['column-definition'])) { + $mapping['columnDefinition'] = (string)$idElement['column-definition']; + } + + if (isset($idElement->options)) { + $mapping['options'] = $this->_parseOptions($idElement->options->children()); + } + + $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->{'custom-id-generator'})) { + $customGenerator = $idElement->{'custom-id-generator'}; + $metadata->setCustomGeneratorDefinition(array( + 'class' => (string) $customGenerator['class'] + )); + } 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->joinColumnToArray($oneToOneElement->{'join-column'}); + } else if (isset($oneToOneElement->{'join-columns'})) { + foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + } + + if (isset($oneToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade); + } + + if (isset($oneToOneElement['orphan-removal'])) { + $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']); + } + + $metadata->mapOneToOne($mapping); + + // Evaluate second level cache + if (isset($oneToOneElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache)); + } + } + } + + // 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'] = $this->evaluateBoolean($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 second level cache + if (isset($oneToManyElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache)); + } + } + } + + // 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->joinColumnToArray($manyToOneElement->{'join-column'}); + } else if (isset($manyToOneElement->{'join-columns'})) { + foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade); + } + + $metadata->mapManyToOne($mapping); + + // Evaluate second level cache + if (isset($manyToOneElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache)); + } + } + } + + // 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'] = $this->evaluateBoolean($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->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($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 second level cache + if (isset($manyToManyElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache)); + } + } + } + + // Evaluate association-overrides + if (isset($xmlRoot->{'attribute-overrides'})) { + foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; + foreach ($overrideElement->field as $field) { + $mapping = $this->columnToArray($field); + $mapping['fieldName'] = $fieldName; + $metadata->setAttributeOverride($fieldName, $mapping); + } + } + } + + // Evaluate association-overrides + if (isset($xmlRoot->{'association-overrides'})) { + foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; + $override = array(); + + // Check for join-columns + if (isset($overrideElement->{'join-columns'})) { + $joinColumns = array(); + foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for join-table + if ($overrideElement->{'join-table'}) { + $joinTable = null; + $joinTableElement = $overrideElement->{'join-table'}; + + $joinTable = array( + 'name' => (string) $joinTableElement['name'], + 'schema' => (string) $joinTableElement['schema'] + ); + + if (isset($joinTableElement->{'join-columns'})) { + foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + if (isset($joinTableElement->{'inverse-join-columns'})) { + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // 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'])); + } + } + + // Evaluate entity listener + if (isset($xmlRoot->{'entity-listeners'})) { + foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) { + $className = (string) $listenerElement['class']; + // Evaluate the listener using naming convention. + if($listenerElement->count() === 0) { + EntityListenerBuilder::bindEntityListener($metadata, $className); + + continue; + } + + foreach ($listenerElement as $callbackElement) { + $eventName = (string) $callbackElement['type']; + $methodName = (string) $callbackElement['method']; + + $metadata->addEntityListener($eventName, $className, $methodName); + } + } + } + } + + /** + * Parses (nested) option elements. + * + * @param SimpleXMLElement $options The XML element. + * + * @return array The options array. + */ + private function _parseOptions(SimpleXMLElement $options) + { + $array = array(); + + /* @var $option SimpleXMLElement */ + foreach ($options as $option) { + if ($option->count()) { + $value = $this->_parseOptions($option->children()); + } else { + $value = (string) $option; + } + + $attr = $option->attributes(); + + if (isset($attr->name)) { + $array[(string) $attr->name] = $value; + } else { + $array[] = $value; + } + } + + return $array; + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given SimpleXMLElement. + * + * @param SimpleXMLElement $joinColumnElement The XML element. + * + * @return array The mapping array. + */ + private function joinColumnToArray(SimpleXMLElement $joinColumnElement) + { + $joinColumn = array( + 'name' => (string)$joinColumnElement['name'], + 'referencedColumnName' => (string)$joinColumnElement['referenced-column-name'] + ); + + if (isset($joinColumnElement['unique'])) { + $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']); + } + + if (isset($joinColumnElement['nullable'])) { + $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']); + } + + if (isset($joinColumnElement['on-delete'])) { + $joinColumn['onDelete'] = (string)$joinColumnElement['on-delete']; + } + + if (isset($joinColumnElement['column-definition'])) { + $joinColumn['columnDefinition'] = (string)$joinColumnElement['column-definition']; + } + + return $joinColumn; + } + + /** + * Parses the given field as array. + * + * @param SimpleXMLElement $fieldMapping + * + * @return array + */ + private function columnToArray(SimpleXMLElement $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'] = $this->evaluateBoolean($fieldMapping['unique']); + } + + if (isset($fieldMapping['nullable'])) { + $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']); + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']); + } + + if (isset($fieldMapping['column-definition'])) { + $mapping['columnDefinition'] = (string) $fieldMapping['column-definition']; + } + + if (isset($fieldMapping->options)) { + $mapping['options'] = $this->_parseOptions($fieldMapping->options->children()); + } + + return $mapping; + } + + /** + * Parse / Normalize the cache configuration + * + * @param SimpleXMLElement $cacheMapping + * + * @return array + */ + private function cacheToArray(SimpleXMLElement $cacheMapping) + { + $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null; + $usage = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null; + + if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) { + throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage)); + } + + if ($usage) { + $usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage); + } + + return array( + 'usage' => $usage, + 'region' => $region, + ); + } + + /** + * Gathers a list of cascade options found in the given cascade element. + * + * @param SimpleXMLElement $cascadeElement The cascade element. + * + * @return array The list of cascade options. + */ + private function _getCascadeMappings(SimpleXMLElement $cascadeElement) + { + $cascades = array(); + /* @var $action SimpleXmlElement */ + 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; + } + } else if (isset($xmlElement->embeddable)) { + foreach ($xmlElement->embeddable as $embeddableElement) { + $embeddableName = (string) $embeddableElement['name']; + $result[$embeddableName] = $embeddableElement; + } + } + + return $result; + } + + /** + * @param mixed $element + * + * @return bool + */ + protected function evaluateBoolean($element) + { + $flag = (string)$element; + + return ($flag == "true" || $flag == "1"); + } +} 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 00000000000..eab510b1e7b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -0,0 +1,790 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ORM\Mapping\MappingException; +use Symfony\Component\Yaml\Yaml; + +/** + * 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 FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + $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 if ($element['type'] == 'embeddable') { + $metadata->isEmbeddedClass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate root level properties + $primaryTable = array(); + + if (isset($element['table'])) { + $primaryTable['name'] = $element['table']; + } + + if (isset($element['schema'])) { + $primaryTable['schema'] = $element['schema']; + } + + // Evaluate second level cache + if (isset($element['cache'])) { + $metadata->enableCache($this->cacheToArray($element['cache'])); + } + + $metadata->setPrimaryTable($primaryTable); + + // 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); + } + } + + // Evaluate named native queries + if (isset($element['namedNativeQueries'])) { + foreach ($element['namedNativeQueries'] as $name => $mappingElement) { + if (!isset($mappingElement['name'])) { + $mappingElement['name'] = $name; + } + $metadata->addNamedNativeQuery(array( + 'name' => $mappingElement['name'], + 'query' => isset($mappingElement['query']) ? $mappingElement['query'] : null, + 'resultClass' => isset($mappingElement['resultClass']) ? $mappingElement['resultClass'] : null, + 'resultSetMapping' => isset($mappingElement['resultSetMapping']) ? $mappingElement['resultSetMapping'] : null, + )); + } + } + + // Evaluate sql result set mappings + if (isset($element['sqlResultSetMappings'])) { + foreach ($element['sqlResultSetMappings'] as $name => $resultSetMapping) { + if (!isset($resultSetMapping['name'])) { + $resultSetMapping['name'] = $name; + } + + $entities = array(); + $columns = array(); + if (isset($resultSetMapping['entityResult'])) { + foreach ($resultSetMapping['entityResult'] as $entityResultElement) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => isset($entityResultElement['entityClass']) ? $entityResultElement['entityClass'] : null, + 'discriminatorColumn' => isset($entityResultElement['discriminatorColumn']) ? $entityResultElement['discriminatorColumn'] : null, + ); + + if (isset($entityResultElement['fieldResult'])) { + foreach ($entityResultElement['fieldResult'] as $fieldResultElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldResultElement['name']) ? $fieldResultElement['name'] : null, + 'column' => isset($fieldResultElement['column']) ? $fieldResultElement['column'] : null, + ); + } + } + + $entities[] = $entityResult; + } + } + + + if (isset($resultSetMapping['columnResult'])) { + foreach ($resultSetMapping['columnResult'] as $columnResultAnnot) { + $columns[] = array( + 'name' => isset($columnResultAnnot['name']) ? $columnResultAnnot['name'] : null, + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + 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' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null, + 'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null, + 'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null, + 'columnDefinition' => isset($discrColumn['columnDefinition']) ? (string)$discrColumn['columnDefinition'] : null + )); + } 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 => $indexYml) { + if ( ! isset($indexYml['name'])) { + $indexYml['name'] = $name; + } + + if (is_string($indexYml['columns'])) { + $index = array('columns' => array_map('trim', explode(',', $indexYml['columns']))); + } else { + $index = array('columns' => $indexYml['columns']); + } + + if (isset($indexYml['flags'])) { + if (is_string($indexYml['flags'])) { + $index['flags'] = array_map('trim', explode(',', $indexYml['flags'])); + } else { + $index['flags'] = $indexYml['flags']; + } + } + + if (isset($indexYml['options'])) { + $index['options'] = $indexYml['options']; + } + + $metadata->table['indexes'][$indexYml['name']] = $index; + } + } + + // Evaluate uniqueConstraints + if (isset($element['uniqueConstraints'])) { + foreach ($element['uniqueConstraints'] as $name => $uniqueYml) { + if ( ! isset($uniqueYml['name'])) { + $uniqueYml['name'] = $name; + } + + if (is_string($uniqueYml['columns'])) { + $unique = array('columns' => array_map('trim', explode(',', $uniqueYml['columns']))); + } else { + $unique = array('columns' => $uniqueYml['columns']); + } + + if (isset($uniqueYml['options'])) { + $unique['options'] = $uniqueYml['options']; + } + + $metadata->table['uniqueConstraints'][$uniqueYml['name']] = $unique; + } + } + + if (isset($element['options'])) { + $metadata->table['options'] = $element['options']; + } + + $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']; + } + + if (isset($idElement['options'])) { + $mapping['options'] = $idElement['options']; + } + + $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['customIdGenerator'])) { + $customGenerator = $idElement['customIdGenerator']; + $metadata->setCustomGeneratorDefinition(array( + 'class' => (string) $customGenerator['class'] + )); + } else if (isset($idElement['tableGenerator'])) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } + } + + // Evaluate fields + if (isset($element['fields'])) { + foreach ($element['fields'] as $name => $fieldMapping) { + + $mapping = $this->columnToArray($name, $fieldMapping); + + 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($mapping['version'])) { + $metadata->setVersionMapping($mapping); + unset($mapping['version']); + } + + $metadata->mapField($mapping); + } + } + + if (isset($element['embedded'])) { + foreach ($element['embedded'] as $name => $embeddedMapping) { + $mapping = array( + 'fieldName' => $name, + 'class' => $embeddedMapping['class'], + 'columnPrefix' => isset($embeddedMapping['columnPrefix']) ? $embeddedMapping['columnPrefix'] : null, + ); + $metadata->mapEmbedded($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->joinColumnToArray($oneToOneElement['joinColumn']); + } else if (isset($oneToOneElement['joinColumns'])) { + foreach ($oneToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinColumns[] = $this->joinColumnToArray($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 second level cache + if (isset($oneToOneElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToOneElement['cache'])); + } + } + } + + // 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 second level cache + if (isset($oneToManyElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToManyElement['cache'])); + } + } + } + + // 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->joinColumnToArray($manyToOneElement['joinColumn']); + } else if (isset($manyToOneElement['joinColumns'])) { + foreach ($manyToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement['cascade'])) { + $mapping['cascade'] = $manyToOneElement['cascade']; + } + + $metadata->mapManyToOne($mapping); + + // Evaluate second level cache + if (isset($manyToOneElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToOneElement['cache'])); + } + } + } + + // 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']; + } + + if (isset($joinTableElement['joinColumns'])) { + foreach ($joinTableElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + if (isset($joinTableElement['inverseJoinColumns'])) { + foreach ($joinTableElement['inverseJoinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($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 second level cache + if (isset($manyToManyElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToManyElement['cache'])); + } + } + } + + // Evaluate associationOverride + if (isset($element['associationOverride']) && is_array($element['associationOverride'])) { + + foreach ($element['associationOverride'] as $fieldName => $associationOverrideElement) { + $override = array(); + + // Check for joinColumn + if (isset($associationOverrideElement['joinColumn'])) { + $joinColumns = array(); + foreach ($associationOverrideElement['joinColumn'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for joinTable + if (isset($associationOverrideElement['joinTable'])) { + + $joinTableElement = $associationOverrideElement['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->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate associationOverride + if (isset($element['attributeOverride']) && is_array($element['attributeOverride'])) { + + foreach ($element['attributeOverride'] as $fieldName => $attributeOverrideElement) { + $mapping = $this->columnToArray($fieldName, $attributeOverrideElement); + $metadata->setAttributeOverride($fieldName, $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)); + } + } + } + + // Evaluate entityListeners + if (isset($element['entityListeners'])) { + foreach ($element['entityListeners'] as $className => $entityListener) { + // Evaluate the listener using naming convention. + if (empty($entityListener)) { + EntityListenerBuilder::bindEntityListener($metadata, $className); + + continue; + } + + foreach ($entityListener as $eventName => $callbackElement){ + foreach ($callbackElement as $methodName) { + $metadata->addEntityListener($eventName, $className, $methodName); + } + } + } + } + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given join column element. + * + * @param array $joinColumnElement The array join column element. + * + * @return array The mapping array. + */ + private function joinColumnToArray($joinColumnElement) + { + $joinColumn = array(); + if (isset($joinColumnElement['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = (string) $joinColumnElement['referencedColumnName']; + } + + if (isset($joinColumnElement['name'])) { + $joinColumn['name'] = (string) $joinColumnElement['name']; + } + + 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; + } + + /** + * Parses the given column as array. + * + * @param string $fieldName + * @param array $column + * + * @return array + */ + private function columnToArray($fieldName, $column) + { + $mapping = array( + 'fieldName' => $fieldName + ); + + if (isset($column['type'])) { + $params = explode('(', $column['type']); + $column['type'] = $params[0]; + $mapping['type'] = $column['type']; + + if (isset($params[1])) { + $column['length'] = (integer) substr($params[1], 0, strlen($params[1]) - 1); + } + } + + if (isset($column['column'])) { + $mapping['columnName'] = $column['column']; + } + + if (isset($column['length'])) { + $mapping['length'] = $column['length']; + } + + if (isset($column['precision'])) { + $mapping['precision'] = $column['precision']; + } + + if (isset($column['scale'])) { + $mapping['scale'] = $column['scale']; + } + + if (isset($column['unique'])) { + $mapping['unique'] = (bool)$column['unique']; + } + + if (isset($column['options'])) { + $mapping['options'] = $column['options']; + } + + if (isset($column['nullable'])) { + $mapping['nullable'] = $column['nullable']; + } + + if (isset($column['version']) && $column['version']) { + $mapping['version'] = $column['version']; + } + + if (isset($column['columnDefinition'])) { + $mapping['columnDefinition'] = $column['columnDefinition']; + } + + return $mapping; + } + + /** + * Parse / Normalize the cache configuration + * + * @param array $cacheMapping + * + * @return array + */ + private function cacheToArray($cacheMapping) + { + $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null; + $usage = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null; + + if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) { + throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage)); + } + + if ($usage) { + $usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage); + } + + return array( + 'usage' => $usage, + 'region' => $region, + ); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + return Yaml::parse(file_get_contents($file)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embeddable.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embeddable.php new file mode 100644 index 00000000000..f14bfac82a6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embeddable.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Embeddable implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embedded.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embedded.php new file mode 100644 index 00000000000..37339108bc9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embedded.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Embedded implements Annotation +{ + /** + * @Required + * @var string + */ + public $class; + + /** + * @var mixed + */ + public $columnPrefix; +} 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 00000000000..edf6ad5a4be --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php @@ -0,0 +1,37 @@ +. + */ + +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/EntityListenerResolver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php new file mode 100644 index 00000000000..2d5ecf714f7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A resolver is used to instantiate an entity listener. + * + * @since 2.4 + * @author Fabio B. Silva + */ +interface EntityListenerResolver +{ + /** + * Clear all instances from the set, or a specific class when given. + * + * @param string $className The fully-qualified class name + * + * @return void + */ + function clear($className = null); + + /** + * Returns a entity listener instance for the given class name. + * + * @param string $className The fully-qualified class name + * + * @return object An entity listener + */ + function resolve($className); + + /** + * Register a entity listener instance. + * + * @param object $object An entity listener + */ + function register($object); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php new file mode 100644 index 00000000000..d9478a4979e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The EntityListeners annotation specifies the callback listener classes to be used for an entity or mapped superclass. + * The EntityListeners annotation may be applied to an entity class or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.4 + * + * @Annotation + * @Target("CLASS") + */ +final class EntityListeners implements Annotation +{ + /** + * Specifies the names of the entity listeners. + * + * @var array + */ + public $value = array(); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php new file mode 100644 index 00000000000..63bbed22fac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References an entity in the SELECT clause of a SQL query. + * If this annotation is used, the SQL statement should select all of the columns that are mapped to the entity object. + * This should include foreign key columns to related entities. + * The results obtained when insufficient data is available are undefined. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class EntityResult implements Annotation +{ + /** + * The class of the result. + * + * @var string + */ + public $entityClass; + + /** + * Maps the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @var array<\Doctrine\ORM\Mapping\FieldResult> + */ + public $fields = array(); + + /** + * Specifies the column name of the column in the SELECT list that is used to determine the type of the entity instance. + * + * @var string + */ + public $discriminatorColumn; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php new file mode 100644 index 00000000000..5e8aa0cd3e0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to map the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class FieldResult implements Annotation +{ + /** + * Name of the column in the SELECT clause. + * + * @var string + */ + public $name; + + /** + * Name of the persistent field or property of the class. + * + * @var string + */ + public $column; +} 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 00000000000..27c03d4bee7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class GeneratedValue implements Annotation +{ + /** + * The type of Id generator. + * + * @var string + * + * @Enum({"AUTO", "SEQUENCE", "TABLE", "IDENTITY", "NONE", "UUID", "CUSTOM"}) + */ + 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 00000000000..313ece31f8c --- /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 00000000000..6c9bcef0d71 --- /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 00000000000..45953a80478 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class Index implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var array + */ + public $columns; + + /** + * @var array + */ + public $flags; + + /** + * @var array + */ + public $options; +} 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 00000000000..de803369a30 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class InheritanceType implements Annotation +{ + /** + * The inheritance type used by the class and its subclasses. + * + * @var string + * + * @Enum({"NONE", "JOINED", "SINGLE_TABLE", "TABLE_PER_CLASS"}) + */ + 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 00000000000..febce917481 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php @@ -0,0 +1,64 @@ +. + */ + +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; + + /** + * Field name used in non-object hydration (array/scalar). + * + * @var string + */ + public $fieldName; +} 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 00000000000..ae096c27503 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php @@ -0,0 +1,32 @@ +. + */ + +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 00000000000..8a440c6143d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +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 00000000000..ca2f53c9eea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php @@ -0,0 +1,66 @@ +. + */ + +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; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + 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 00000000000..d3414e6a956 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToOne implements Annotation +{ + /** + * @var string + */ + public $targetEntity; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + 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 00000000000..74588107d89 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php @@ -0,0 +1,32 @@ +. + */ + +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 00000000000..9e6b4b33fdb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php @@ -0,0 +1,812 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.0 + */ +class MappingException extends \Doctrine\ORM\ORMException +{ + /** + * @return MappingException + */ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + /** + * @param string $entityName + * + * @return MappingException + */ + public static function identifierRequired($entityName) + { + if (false !== ($parent = get_parent_class($entityName))) { + return new self(sprintf( + 'No identifier/primary key specified for Entity "%s" sub class of "%s". Every Entity must have an identifier/primary key.', + $entityName, $parent + )); + } + + return new self(sprintf( + 'No identifier/primary key specified for Entity "%s". Every Entity must have an identifier/primary key.', + $entityName + )); + + } + + /** + * @param string $entityName + * @param string $type + * + * @return MappingException + */ + public static function invalidInheritanceType($entityName, $type) + { + return new self("The inheritance type '$type' specified for '$entityName' does not exist."); + } + + /** + * @return MappingException + */ + public static function generatorNotAllowedWithCompositeId() + { + return new self("Id generators can't be used with a composite id."); + } + + /** + * @param string $entity + * + * @return MappingException + */ + public static function missingFieldName($entity) + { + return new self("The field or association mapping misses the 'fieldName' attribute in entity '$entity'."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingTargetEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingSourceEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingEmbeddedClass($fieldName) + { + return new self("The embed mapping '$fieldName' misses the 'class' attribute."); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return MappingException + */ + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } + + /** + * Exception for invalid property name override. + * + * @param string $className The entity's name. + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidOverrideFieldName($className, $fieldName) + { + return new self("Invalid field override named '$fieldName' for class '$className'."); + } + + /** + * Exception for invalid property type override. + * + * @param string $className The entity's name. + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidOverrideFieldType($className, $fieldName) + { + return new self("The column type of attribute '$fieldName' on class '$className' could not be changed."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return MappingException + */ + public static function mappingNotFound($className, $fieldName) + { + return new self("No mapping found for field '$fieldName' on class '$className'."); + } + + /** + * @param string $className + * @param string $queryName + * + * @return MappingException + */ + public static function queryNotFound($className, $queryName) + { + return new self("No query found named '$queryName' on class '$className'."); + } + + /** + * @param string $className + * @param string $resultName + * + * @return MappingException + */ + public static function resultMappingNotFound($className, $resultName) + { + return new self("No result set mapping found named '$resultName' on class '$className'."); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function emptyQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" could not be empty.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForQueryMapping($className) + { + return new self("Query name on entity class '$className' is not defined."); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function missingQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.' requires a result class or result set mapping.'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function missingResultSetMappingEntity($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a entity class name.'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function missingResultSetMappingFieldName($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a field name.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForSqlResultSetMapping($className) + { + return new self("Result set mapping name on entity class '$className' is not defined."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function oneToManyRequiresMappedBy($fieldName) + { + return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + 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 + * + * @return MappingException + */ + 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 + * + * @return MappingException + */ + public static function reflectionFailure($entity, \ReflectionException $previousException) + { + return new self('An error occurred in ' . $entity, 0, $previousException); + } + + /** + * @param string $className + * @param string $joinColumn + * + * @return MappingException + */ + 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.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function classIsNotAValidEntityOrMappedSuperClass($className) + { + if (false !== ($parent = get_parent_class($className))) { + return new self(sprintf( + 'Class "%s" sub class of "%s" is not a valid entity or mapped super class.', + $className, $parent + )); + } + + return new self(sprintf( + 'Class "%s" is not a valid entity or mapped super class.', + $className + )); + } + + /** + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function propertyTypeIsRequired($className, $propertyName) + { + return new self("The attribute 'type' is required for the column description of property ".$className."::\$".$propertyName."."); + } + + /** + * @param string $className + * + * @return MappingException + */ + 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. + * + * @return MappingException + */ + public static function duplicateFieldMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $fieldName + * + * @return MappingException + */ + public static function duplicateAssociationMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function duplicateQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function duplicateResultSetMapping($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * + * @return MappingException + */ + public static function singleIdNotAllowedOnCompositePrimaryKey($entity) + { + return new self('Single id is not allowed on composite primary key in entity '.$entity); + } + + /** + * @param string $entity + * @param string $fieldName + * @param string $unsupportedType + * + * @return MappingException + */ + public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) + { + return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") ' + .'is not supported by Doctrine.' + ); + } + + /** + * @param string|null $path + * + * @return MappingException + */ + 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!' + ); + } + + /** + * Returns 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 MappingException + */ + public static function invalidClassInDiscriminatorMap($className, $owningClass) + { + return new self( + "Entity class '$className' used in the discriminator map of class '$owningClass' ". + "does not exist." + ); + } + + /** + * @param string $className + * @param array $entries + * @param array $map + * + * @return MappingException + */ + public static function duplicateDiscriminatorEntry($className, array $entries, array $map) + { + return new self( + "The entries " . implode(', ', $entries) . " in discriminator map of class '" . $className . "' is duplicated. " . + "If the discriminator map is automatically generated you have to convert it to an explicit discriminator map now. " . + "The entries of the current map are: @DiscriminatorMap({" . implode(', ', array_map( + function($a, $b) { return "'$a': '$b'"; }, array_keys($map), array_values($map) + )) . "})" + ); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingDiscriminatorMap($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator map was defined."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingDiscriminatorColumn($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator column was defined."); + } + + /** + * @param string $className + * @param string $type + * + * @return MappingException + */ + 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!"); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForDiscriminatorColumns($className) + { + return new self("Discriminator column name on entity class '$className' is not defined."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return MappingException + */ + public static function cannotVersionIdField($className, $fieldName) + { + return new self("Setting Id field '$fieldName' as versionable in entity class '$className' is not supported."); + } + + /** + * @param string $className + * @param string $fieldName + * @param string $type + * + * @return MappingException + */ + 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 MappingException + */ + public static function duplicateColumnName($className, $columnName) + { + return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalToManyAssociationOnMappedSuperclass($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 MappingException + */ + 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."'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function noSingleAssociationJoinColumnFound($className, $field) + { + return new self("'$className#$field' is not an association with a single join column."); + } + + /** + * @param string $className + * @param string $column + * + * @return MappingException + */ + 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."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + 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'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + 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."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalInverseIdentifierAssociation($className, $field) + { + return new self("An inverse association is not allowed to be identifier in '$className#$field'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalToManyIdentifierAssociation($className, $field) + { + return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function noInheritanceOnMappedSuperClass($className) + { + return new self("It is not supported to define inheritance information on a mapped superclass '" . $className . "'."); + } + + /** + * @param string $className + * @param string $rootClassName + * + * @return MappingException + */ + 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 hierarchy. Alternatively you can make '".$className."' an abstract class " . + "to avoid this exception from occurring." + ); + } + + /** + * @param string $className + * @param string $methodName + * + * @return MappingException + */ + public static function lifecycleCallbackMethodNotFound($className, $methodName) + { + return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback."); + } + + /** + * @param string $listenerName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function entityListenerClassNotFound($listenerName, $className) + { + return new self(sprintf('Entity Listener "%s" declared on "%s" not found.', $listenerName, $className)); + } + + /** + * @param string $listenerName + * @param string $methodName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function entityListenerMethodNotFound($listenerName, $methodName, $className) + { + return new self(sprintf('Entity Listener "%s" declared on "%s" has no method "%s".', $listenerName, $className, $methodName)); + } + + /** + * @param string $listenerName + * @param string $methodName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function duplicateEntityListener($listenerName, $methodName, $className) + { + return new self(sprintf('Entity Listener "%s#%s()" in "%s" was already declared, but it must be declared only once.', $listenerName, $methodName, $className)); + } + + /** + * @param string $className + * @param string $annotation + * + * @return MappingException + */ + public static function invalidFetchMode($className, $annotation) + { + return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'"); + } + + /** + * @param string $className + * + * @return MappingException + */ + 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."); + } + + /** + * @param string $targetEntity + * @param string $sourceEntity + * @param string $associationName + * + * @return MappingException + */ + public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName) + { + return new self("The target-entity " . $targetEntity . " cannot be found in '" . $sourceEntity."#".$associationName."'."); + } + + /** + * @param array $cascades + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function invalidCascadeOption(array $cascades, $className, $propertyName) + { + $cascades = implode(", ", array_map(function ($e) { return "'" . $e . "'"; }, $cascades)); + return new self(sprintf( + "You have specified invalid cascade options for %s::$%s: %s; available options: 'remove', 'persist', 'refresh', 'merge', and 'detach'", + $className, + $propertyName, + $cascades + )); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingSequenceName($className) + { + return new self( + sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className) + ); + } + + /** + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function infiniteEmbeddableNesting($className, $propertyName) + { + return new self( + sprintf( + 'Infinite nesting detected for embedded property %s::%s. ' . + 'You cannot embed an embeddable from the same type inside an embeddable.', + $className, + $propertyName + ) + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php new file mode 100644 index 00000000000..7f5460a81e9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of native SQL named queries. + * The NamedNativeQueries annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class NamedNativeQueries implements Annotation +{ + /** + * One or more NamedNativeQuery annotations. + * + * @var array<\Doctrine\ORM\Mapping\NamedNativeQuery> + */ + public $value = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php new file mode 100644 index 00000000000..f336c99171d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify a native SQL named query. + * The NamedNativeQuery annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class NamedNativeQuery implements Annotation +{ + /** + * The name used to refer to the query with the EntityManager methods that create query objects. + * + * @var string + */ + public $name; + + /** + * The SQL query string. + * + * @var string + */ + public $query; + + /** + * The class of the result. + * + * @var string + */ + public $resultClass; + + /** + * The name of a SqlResultSetMapping, as defined in metadata. + * + * @var string + */ + public $resultSetMapping; +} 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 00000000000..5fce0727b28 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php @@ -0,0 +1,32 @@ +. + */ + +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 00000000000..c4e6cd528fb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php @@ -0,0 +1,37 @@ +. + */ + +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/NamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php new file mode 100644 index 00000000000..8845cb1ed5f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A set of rules for determining the physical column and table names + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +interface NamingStrategy +{ + /** + * Returns a table name for an entity class. + * + * @param string $className The fully-qualified class name. + * + * @return string A table name. + */ + function classToTableName($className); + + /** + * Returns a column name for a property. + * + * @param string $propertyName A property name. + * @param string|null $className The fully-qualified class name. + * + * @return string A column name. + */ + function propertyToColumnName($propertyName, $className = null); + + /** + * Returns a column name for an embedded property. + * + * @param string $propertyName + * @param string $embeddedColumnName + * + * @return string + */ + function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null); + + /** + * Returns the default reference column name. + * + * @return string A column name. + */ + function referenceColumnName(); + + /** + * Returns a join column name for a property. + * + * @param string $propertyName A property name. + * @param string|null $className The fully-qualified class name. + * This parameter is omitted from the signature due to BC + * + * @return string A join column name. + */ + function joinColumnName($propertyName/*, $className = null*/); + + /** + * Returns a join table name. + * + * @param string $sourceEntity The source entity. + * @param string $targetEntity The target entity. + * @param string|null $propertyName A property name. + * + * @return string A join table name. + */ + function joinTableName($sourceEntity, $targetEntity, $propertyName = null); + + /** + * Returns the foreign key column name for the given parameters. + * + * @param string $entityName An entity. + * @param string|null $referencedColumnName A property. + * + * @return string A join column name. + */ + function joinKeyColumnName($entityName, $referencedColumnName = null); +} 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 00000000000..4b2465718e1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToMany implements Annotation +{ + /** + * @var string + */ + public $mappedBy; + + /** + * @var string + */ + public $targetEntity; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + 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 00000000000..b2ab81f88d9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php @@ -0,0 +1,61 @@ +. + */ + +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; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + 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 00000000000..ad1b7a8f714 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php @@ -0,0 +1,32 @@ +. + */ + +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 00000000000..2f8e9932e35 --- /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 00000000000..2aea7194911 --- /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 00000000000..321c4bd547b --- /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 00000000000..a55f7072a69 --- /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 00000000000..6697d372c37 --- /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 00000000000..fea05be6d3d --- /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 00000000000..29822edacc9 --- /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 00000000000..290df72e04a --- /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/QuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php new file mode 100644 index 00000000000..1a21ff1bb16 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * A set of rules for determining the column, alias and table quotes. + * + * @since 2.3 + * @author Fabio B. Silva + */ +interface QuoteStrategy +{ + /** + * Gets the (possibly quoted) column name for safe use in an SQL statement. + * + * @param string $fieldName + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) primary table name for safe use in an SQL statement. + * + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getTableName(ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) sequence name for safe use in an SQL statement. + * + * @param array $definition + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) name of the join table. + * + * @param array $association + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) join column name. + * + * @param array $joinColumn + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) join column name. + * + * @param array $joinColumn + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return array + */ + function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the column alias. + * + * @param string $columnName + * @param integer $counter + * @param AbstractPlatform $platform + * @param ClassMetadata|null $class + * + * @return string + */ + function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null); + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php new file mode 100644 index 00000000000..6ac7f11c3fd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Reflection; + +use Doctrine\Common\Persistence\Mapping\ReflectionService; +use ReflectionClass; +use ReflectionProperty; + +/** + * Utility class to retrieve all reflection instance properties of a given class, including + * private inherited properties and transient properties. + * + * @private This API is for internal use only + * + * @author Marco Pivetta + */ +final class ReflectionPropertiesGetter +{ + /** + * @var ReflectionProperty[][] indexed by class name and property internal name + */ + private $properties = []; + + /** + * @var ReflectionService + */ + private $reflectionService; + + /** + * @param ReflectionService $reflectionService + */ + public function __construct(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * @param $className + * + * @return ReflectionProperty[] indexed by property internal name + */ + public function getProperties($className) + { + if (isset($this->properties[$className])) { + return $this->properties[$className]; + } + + return $this->properties[$className] = call_user_func_array( + 'array_merge', + // first merge because `array_merge` expects >= 1 params + array_merge( + [[]], + array_map( + [$this, 'getClassProperties'], + $this->getHierarchyClasses($className) + ) + ) + ); + } + + /** + * @param string $className + * + * @return ReflectionClass[] + */ + private function getHierarchyClasses($className) + { + $classes = []; + $parentClassName = $className; + + while ($parentClassName && $currentClass = $this->reflectionService->getClass($parentClassName)) { + $classes[] = $currentClass; + $parentClassName = null; + + if ($parentClass = $currentClass->getParentClass()) { + $parentClassName = $parentClass->getName(); + } + } + + return $classes; + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return ReflectionProperty[] + */ + private function getClassProperties(ReflectionClass $reflectionClass) + { + $properties = $reflectionClass->getProperties(); + + return array_filter( + array_filter(array_map( + [$this, 'getAccessibleProperty'], + array_combine( + array_map([$this, 'getLogicalName'], $properties), + $properties + ) + )), + [$this, 'isInstanceProperty'] + ); + } + + /** + * @param ReflectionProperty $reflectionProperty + * + * @return bool + */ + private function isInstanceProperty(ReflectionProperty $reflectionProperty) + { + return ! $reflectionProperty->isStatic(); + } + + /** + * @param ReflectionProperty $property + * + * @return null|ReflectionProperty + */ + private function getAccessibleProperty(ReflectionProperty $property) + { + return $this->reflectionService->getAccessibleProperty( + $property->getDeclaringClass()->getName(), + $property->getName() + ); + } + + /** + * @param ReflectionProperty $property + * + * @return string + */ + private function getLogicalName(ReflectionProperty $property) + { + $propertyName = $property->getName(); + + if ($property->isPublic()) { + return $propertyName; + } + + if ($property->isProtected()) { + return "\0*\0" . $propertyName; + } + + return "\0" . $property->getDeclaringClass()->getName() . "\0" . $propertyName; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php new file mode 100644 index 00000000000..b224fff4eb5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php @@ -0,0 +1,101 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\Instantiator\Instantiator; +use ReflectionProperty; + +/** + * Acts as a proxy to a nested Property structure, making it look like + * just a single scalar property. + * + * This way value objects "just work" without UnitOfWork, Persisters or Hydrators + * needing any changes. + * + * TODO: Move this class into Common\Reflection + */ +class ReflectionEmbeddedProperty extends ReflectionProperty +{ + /** + * @var ReflectionProperty reflection property of the class where the embedded object has to be put + */ + private $parentProperty; + + /** + * @var ReflectionProperty reflection property of the embedded object + */ + private $childProperty; + + /** + * @var string name of the embedded class to be eventually instantiated + */ + private $embeddedClass; + + /** + * @var Instantiator|null + */ + private $instantiator; + + /** + * @param ReflectionProperty $parentProperty + * @param ReflectionProperty $childProperty + * @param string $embeddedClass + */ + public function __construct(ReflectionProperty $parentProperty, ReflectionProperty $childProperty, $embeddedClass) + { + $this->parentProperty = $parentProperty; + $this->childProperty = $childProperty; + $this->embeddedClass = (string) $embeddedClass; + + parent::__construct($childProperty->getDeclaringClass()->getName(), $childProperty->getName()); + } + + /** + * {@inheritDoc} + */ + public function getValue($object = null) + { + $embeddedObject = $this->parentProperty->getValue($object); + + if (null === $embeddedObject) { + return null; + } + + return $this->childProperty->getValue($embeddedObject); + } + + /** + * {@inheritDoc} + */ + public function setValue($object, $value = null) + { + $embeddedObject = $this->parentProperty->getValue($object); + + if (null === $embeddedObject) { + $this->instantiator = $this->instantiator ?: new Instantiator(); + + $embeddedObject = $this->instantiator->instantiate($this->embeddedClass); + + $this->parentProperty->setValue($object, $embeddedObject); + } + + $this->childProperty->setValue($embeddedObject, $value); + } +} 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 00000000000..ba1c45b6425 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php @@ -0,0 +1,42 @@ +. + */ + +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/SqlResultSetMapping.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php new file mode 100644 index 00000000000..f5ead7c0f22 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The SqlResultSetMapping annotation is used to specify the mapping of the result of a native SQL query. + * The SqlResultSetMapping annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class SqlResultSetMapping implements Annotation +{ + /** + * The name given to the result set mapping, and used to refer to it in the methods of the Query API. + * + * @var string + */ + public $name; + + /** + * Specifies the result set mapping to entities. + * + * @var array<\Doctrine\ORM\Mapping\EntityResult> + */ + public $entities = array(); + + /** + * Specifies the result set mapping to scalar values. + * + * @var array<\Doctrine\ORM\Mapping\ColumnResult> + */ + public $columns = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php new file mode 100644 index 00000000000..c21b2ab820b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of mappings. + * The SqlResultSetMappings annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class SqlResultSetMappings implements Annotation +{ + /** + * One or more SqlResultSetMapping annotations. + * + * @var array<\Doctrine\ORM\Mapping\SqlResultSetMapping> + */ + public $value = array(); +} 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 00000000000..f9f8d4a656d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php @@ -0,0 +1,52 @@ +. + */ + +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; + + /** + * @var array + */ + public $options = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php new file mode 100644 index 00000000000..543d92ba31c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Naming strategy implementing the underscore naming convention. + * Converts 'MyEntity' to 'my_entity' or 'MY_ENTITY'. + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +class UnderscoreNamingStrategy implements NamingStrategy +{ + /** + * @var integer + */ + private $case; + + /** + * Underscore naming strategy construct. + * + * @param integer $case CASE_LOWER | CASE_UPPER + */ + public function __construct($case = CASE_LOWER) + { + $this->case = $case; + } + + /** + * @return integer CASE_LOWER | CASE_UPPER + */ + public function getCase() + { + return $this->case; + } + + /** + * Sets string case CASE_LOWER | CASE_UPPER. + * Alphabetic characters converted to lowercase or uppercase. + * + * @param integer $case + * + * @return void + */ + public function setCase($case) + { + $this->case = $case; + } + + /** + * {@inheritdoc} + */ + public function classToTableName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, '\\') + 1); + } + + return $this->underscore($className); + } + + /** + * {@inheritdoc} + */ + public function propertyToColumnName($propertyName, $className = null) + { + return $this->underscore($propertyName); + } + + /** + * {@inheritdoc} + */ + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) + { + return $this->underscore($propertyName).'_'.$embeddedColumnName; + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return $this->case === CASE_UPPER ? 'ID' : 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName, $className = null) + { + return $this->underscore($propertyName) . '_' . $this->referenceColumnName(); + } + + /** + * {@inheritdoc} + */ + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return $this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity); + } + + /** + * {@inheritdoc} + */ + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return $this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName()); + } + + /** + * @param string $string + * + * @return string + */ + private function underscore($string) + { + $string = preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $string); + + if ($this->case === CASE_UPPER) { + return strtoupper($string); + } + + return strtolower($string); + } +} 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 00000000000..f117d1873e8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class UniqueConstraint implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var array + */ + public $columns; + + /** + * @var array + */ + public $options; +} 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 00000000000..a2377027950 --- /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 00000000000..b19f8180564 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Represents a native SQL query. + * + * @author Roman Borschel + * @since 2.0 + */ +final class NativeQuery extends AbstractQuery +{ + /** + * @var string + */ + 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() + { + $parameters = array(); + $types = array(); + + foreach ($this->getParameters() as $parameter) { + $name = $parameter->getName(); + $value = $this->processParameterValue($parameter->getValue()); + $type = ($parameter->getValue() === $value) + ? $parameter->getType() + : Query\ParameterTypeInferer::inferType($value); + + $parameters[$name] = $value; + $types[$name] = $type; + } + + if ($parameters && is_int(key($parameters))) { + ksort($parameters); + ksort($types); + + $parameters = array_values($parameters); + $types = array_values($types); + } + + return $this->_em->getConnection()->executeQuery( + $this->_sql, $parameters, $types, $this->_queryCacheProfile + ); + } +} 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 00000000000..2cbac8e9d95 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly does not return any results. + * + * @author robo + * @since 2.0 + */ +class NoResultException extends UnexpectedResultException +{ + /** + * Constructor. + */ + public function __construct() + { + parent::__construct('No result was found for query although at least one row was expected.'); + } +} 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 00000000000..55b71300058 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly returns more than one result. + * + * @author robo + * @since 2.0 + */ +class NonUniqueResultException extends UnexpectedResultException +{ +} 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 00000000000..d4a729d7e2f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php @@ -0,0 +1,328 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Cache\Cache as CacheDriver; +use Exception; + +/** + * Base exception class for all ORM exceptions. + * + * @author Roman Borschel + * @since 2.0 + */ +class ORMException extends Exception +{ + /** + * @return ORMException + */ + public static function missingMappingDriverImpl() + { + return new self("It's a requirement to specify a Metadata Driver and pass it ". + "to Doctrine\\ORM\\Configuration::setMetadataDriverImpl()."); + } + + /** + * @param string $queryName + * + * @return ORMException + */ + public static function namedQueryNotFound($queryName) + { + return new self('Could not find a named query by the name "' . $queryName . '"'); + } + + /** + * @param string $nativeQueryName + * + * @return ORMException + */ + public static function namedNativeQueryNotFound($nativeQueryName) + { + return new self('Could not find a named native query by the name "' . $nativeQueryName . '"'); + } + + /** + * @param object $entity + * @param object $relatedEntity + * + * @return ORMException + */ + 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) this means you have to call " . + "EntityManager#flush() between both persist operations." + ); + } + + /** + * @param object $entity + * @param string $field + * + * @return ORMException + */ + 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." + ); + } + + /** + * @param string $field + * + * @return ORMException + */ + public static function unrecognizedField($field) + { + return new self("Unrecognized field: $field"); + } + + /** + * + * @param string $class + * @param string $association + * @param string $given + * @param string $expected + * + * @return \Doctrine\ORM\ORMInvalidArgumentException + */ + public static function unexpectedAssociationValue($class, $association, $given, $expected) + { + return new self(sprintf('Found entity of type %s on association %s#%s, but expecting %s', $given, $class, $association, $expected)); + } + + /** + * @param string $className + * @param string $field + * + * @return ORMException + */ + public static function invalidOrientation($className, $field) + { + return new self("Invalid order by orientation specified for " . $className . "#" . $field); + } + + /** + * @param string $mode + * + * @return ORMException + */ + public static function invalidFlushMode($mode) + { + return new self("'$mode' is an invalid flush mode."); + } + + /** + * @return ORMException + */ + public static function entityManagerClosed() + { + return new self("The EntityManager is closed."); + } + + /** + * @param string $mode + * + * @return ORMException + */ + public static function invalidHydrationMode($mode) + { + return new self("'$mode' is an invalid hydration mode."); + } + + /** + * @return ORMException + */ + public static function mismatchedEventManager() + { + return new self("Cannot use different EventManager instances for EntityManager and Connection."); + } + + /** + * @param string $methodName + * + * @return ORMException + */ + public static function findByRequiresParameter($methodName) + { + return new self("You need to pass a parameter to '".$methodName."'"); + } + + /** + * @param string $entityName + * @param string $fieldName + * @param string $method + * + * @return ORMException + */ + 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" + ); + } + + /** + * @param string $entityName + * @param string $associationFieldName + * + * @return ORMException + */ + 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." + ); + } + + /** + * @return ORMException + */ + public static function invalidResultCacheDriver() + { + return new self("Invalid result cache driver; it must implement Doctrine\\Common\\Cache\\Cache."); + } + + /** + * @return ORMException + */ + public static function notSupported() + { + return new self("This behaviour is (currently) not supported by Doctrine 2"); + } + + /** + * @return ORMException + */ + public static function queryCacheNotConfigured() + { + return new self('Query Cache is not configured.'); + } + + /** + * @return ORMException + */ + public static function metadataCacheNotConfigured() + { + return new self('Class Metadata Cache is not configured.'); + } + + /** + * @param \Doctrine\Common\Cache\Cache $cache + * + * @return ORMException + */ + public static function queryCacheUsesNonPersistentCache(CacheDriver $cache) + { + return new self('Query Cache uses a non-persistent cache driver, ' . get_class($cache) . '.'); + } + + /** + * @param \Doctrine\Common\Cache\Cache $cache + * + * @return ORMException + */ + public static function metadataCacheUsesNonPersistentCache(CacheDriver $cache) + { + return new self('Metadata Cache uses a non-persistent cache driver, ' . get_class($cache) . '.'); + } + + /** + * @return ORMException + */ + public static function proxyClassesAlwaysRegenerating() + { + return new self('Proxy Classes are always regenerating.'); + } + + /** + * @param string $entityNamespaceAlias + * + * @return ORMException + */ + public static function unknownEntityNamespace($entityNamespaceAlias) + { + return new self( + "Unknown Entity namespace alias '$entityNamespaceAlias'." + ); + } + + /** + * @param string $className + * + * @return ORMException + */ + public static function invalidEntityRepository($className) + { + return new self("Invalid repository class '".$className."'. It must be a Doctrine\Common\Persistence\ObjectRepository."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return ORMException + */ + public static function missingIdentifierField($className, $fieldName) + { + return new self("The identifier $fieldName is missing for a query of " . $className); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return ORMException + */ + public static function unrecognizedIdentifierFields($className, $fieldNames) + { + return new self( + "Unrecognized identifier fields: '" . implode("', '", $fieldNames) . "' " . + "are not present on class '" . $className . "'." + ); + } + + /** + * @param string $functionName + * + * @return ORMException + */ + public static function overwriteInternalDQLFunctionNotAllowed($functionName) + { + return new self("It is not allowed to overwrite internal function '$functionName' in the DQL parser through user-defined functions."); + } + + /** + * @return ORMException + */ + public static function cantUseInOperatorOnCompositeKeys() + { + return new self("Can't use IN operator on entities that have composite keys."); + } +} 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 00000000000..02c0f99aafb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php @@ -0,0 +1,226 @@ +. + */ + +namespace Doctrine\ORM; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Contains exception messages for all invalid lifecycle state exceptions inside UnitOfWork + * + * @author Benjamin Eberlei + */ +class ORMInvalidArgumentException extends \InvalidArgumentException +{ + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertForManagedEntity($entity) + { + return new self("A managed+dirty entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertForRemovedEntity($entity) + { + return new self("Removed entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertTwice($entity) + { + return new self("Entity " . self::objToStr($entity) . " can not be scheduled for insertion twice."); + } + + /** + * @param string $className + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function entityWithoutIdentity($className, $entity) + { + return 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." + ); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + 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"); + } + + /** + * @param array $assoc + * @param object $entry + * + * @return ORMInvalidArgumentException + */ + 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\"})." + . (method_exists($entry, '__toString') ? + "": + " If you cannot find out which entity causes the problem" + ." implement '" . $assoc['targetEntity'] . "#__toString()' to get a clue.")); + } + + /** + * @param array $assoc + * @param object $entry + * + * @return ORMInvalidArgumentException + */ + static public function detachedEntityFoundThroughRelationship(array $assoc, $entry) + { + return 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."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function entityNotManaged($entity) + { + return 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"); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function entityHasNoIdentity($entity, $operation) + { + return new self("Entity has no identity, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function entityIsRemoved($entity, $operation) + { + return new self("Entity is removed, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function detachedEntityCannot($entity, $operation) + { + return new self("Detached entity " . self::objToStr($entity) . " cannot be " . $operation); + } + + /** + * @param string $context + * @param mixed $given + * @param int $parameterIndex + * + * @return ORMInvalidArgumentException + */ + public static function invalidObject($context, $given, $parameterIndex = 1) + { + return new self($context . ' expects parameter ' . $parameterIndex . + ' to be an entity object, '. gettype($given) . ' given.'); + } + + /** + * @return ORMInvalidArgumentException + */ + public static function invalidCompositeIdentifier() + { + return new self("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 separately."); + } + + /** + * @return ORMInvalidArgumentException + */ + public static function invalidIdentifierBindingEntity() + { + return new self("Binding entities to query parameters only allowed for entities that have an identifier."); + } + + /** + * @param ClassMetadata $targetClass + * @param array $assoc + * @param mixed $actualValue + * + * @return self + */ + public static function invalidAssociation(ClassMetadata $targetClass, $assoc, $actualValue) + { + $expectedType = 'Doctrine\Common\Collections\Collection|array'; + + if (($assoc['type'] & ClassMetadata::TO_ONE) > 0) { + $expectedType = $targetClass->getName(); + } + + return new self(sprintf( + 'Expected value of type "%s" for association field "%s#$%s", got "%s" instead.', + $expectedType, + $assoc['sourceEntity'], + $assoc['fieldName'], + is_object($actualValue) ? get_class($actualValue) : gettype($actualValue) + )); + } + + /** + * 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); + } +} 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 00000000000..ecd5445b71b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php @@ -0,0 +1,90 @@ +. + */ + +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 +{ + /** + * @var object|null + */ + private $entity; + + /** + * @param string $msg + * @param object $entity + */ + public function __construct($msg, $entity) + { + parent::__construct($msg); + $this->entity = $entity; + } + + /** + * Gets the entity that caused the exception. + * + * @return object|null + */ + public function getEntity() + { + return $this->entity; + } + + /** + * @param object $entity + * + * @return OptimisticLockException + */ + public static function lockFailed($entity) + { + return new self("The optimistic lock on an entity failed.", $entity); + } + + /** + * @param object $entity + * @param int $expectedLockVersion + * @param int $actualLockVersion + * + * @return OptimisticLockException + */ + public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion) + { + $expectedLockVersion = ($expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion; + $actualLockVersion = ($actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion; + return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); + } + + /** + * @param string $entityName + * + * @return OptimisticLockException + */ + public static function notVersioned($entityName) + { + return new self("Cannot obtain optimistic lock on unversioned entity " . $entityName, null); + } +} 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 00000000000..83eab8cf1ab --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php @@ -0,0 +1,709 @@ +. + */ + +namespace Doctrine\ORM; + +use Closure; +use Doctrine\Common\Collections\AbstractLazyCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * 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 + * @author Stefano Rodriguez + */ +final class PersistentCollection extends AbstractLazyCollection implements Selectable +{ + /** + * 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\EntityManagerInterface + */ + 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. + * + * @var ClassMetadata + */ + 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; + + /** + * Creates a new persistent collection. + * + * @param EntityManagerInterface $em The EntityManager the collection will be associated with. + * @param ClassMetadata $class The class descriptor of the entity type of this collection. + * @param Collection $collection The collection elements. + */ + public function __construct(EntityManagerInterface $em, $class, Collection $collection) + { + $this->collection = $collection; + $this->em = $em; + $this->typeClass = $class; + $this->initialized = true; + } + + /** + * 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 array $assoc + * + * @return void + */ + 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; + } + + /** + * @return Mapping\ClassMetadata + */ + 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. + * + * @return void + */ + public function hydrateAdd($element) + { + $this->collection->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 $element The element to set. + * + * @return void + */ + public function hydrateSet($key, $element) + { + $this->collection->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. + * + * @return void + */ + public function initialize() + { + if ($this->initialized || ! $this->association) { + return; + } + + $this->doInitialize(); + + $this->initialized = true; + } + + /** + * INTERNAL: + * Tells this collection to take a snapshot of its current state. + * + * @return void + */ + public function takeSnapshot() + { + $this->snapshot = $this->collection->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->collection->toArray(), + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: + * getInsertDiff + * + * @return array + */ + public function getInsertDiff() + { + return array_udiff_assoc( + $this->collection->toArray(), + $this->snapshot, + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: Gets the association mapping of the collection. + * + * @return array + */ + public function getMapping() + { + return $this->association; + } + + /** + * Marks this collection as changed/dirty. + * + * @return void + */ + 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. + * + * @return void + */ + public function setDirty($dirty) + { + $this->isDirty = $dirty; + } + + /** + * Sets the initialized flag of the collection, forcing it into that state. + * + * @param boolean $bool + * + * @return void + */ + public function setInitialized($bool) + { + $this->initialized = $bool; + } + + /** + * {@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. + $removed = parent::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->collection->contains($element)) { + return $this->collection->removeElement($element); + } + + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + if ($persister->removeElement($this, $element)) { + return $element; + } + + return null; + } + + $removed = parent::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) + { + if (! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY + && isset($this->association['indexBy'])) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $this->collection->containsKey($key) || $persister->containsKey($this, $key); + } + + return parent::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->collection->contains($element) || $persister->contains($this, $element); + } + + return parent::contains($element); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + if ( ! $this->initialized + && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY + && isset($this->association['indexBy']) + ) { + if (!$this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->association['indexBy'])) { + return $this->em->find($this->typeClass->name, $key); + } + + return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key); + } + + return parent::get($key); + } + + /** + * {@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->collection->count() : 0); + } + + return parent::count(); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + parent::set($key, $value); + + $this->changed(); + } + + /** + * {@inheritdoc} + */ + public function add($value) + { + $this->collection->add($value); + + $this->changed(); + + return true; + } + + /* ArrayAccess implementation */ + + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + + return $this->set($offset, $value); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + return $this->collection->isEmpty() && $this->count() === 0; + } + + /** + * {@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->collection as $element) { + $uow->scheduleOrphanRemoval($element); + } + } + + $this->collection->clear(); + + $this->initialized = true; // direct call, {@link initialize()} is too expensive + + if ($this->association['isOwningSide'] && $this->owner) { + $this->changed(); + + $uow->scheduleCollectionDeletion($this); + + $this->takeSnapshot(); + } + } + + /** + * Called by PHP when this collection is serialized. Ensures that only the + * elements are properly serialized. + * + * Internal note: Tried to implement Serializable first but that did not work well + * with circular references. This solution seems simpler and works well. + * + * @return array + */ + public function __sleep() + { + return array('collection', 'initialized'); + } + + /** + * Extracts 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|null $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); + } + + return parent::slice($offset, $length); + } + + /** + * Cleans up 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. + * + * @return void + */ + public function __clone() + { + if (is_object($this->collection)) { + $this->collection = clone $this->collection; + } + + $this->initialize(); + + $this->owner = null; + $this->snapshot = array(); + + $this->changed(); + } + + /** + * Selects all elements from a selectable that match the expression and + * return a new collection containing these elements. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return Collection + * + * @throws \RuntimeException + */ + public function matching(Criteria $criteria) + { + if ($this->isDirty) { + $this->initialize(); + } + + if ($this->initialized) { + return $this->collection->matching($criteria); + } + + if ($this->association['type'] === ClassMetadata::MANY_TO_MANY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return new ArrayCollection($persister->loadCriteria($this, $criteria)); + } + + $builder = Criteria::expr(); + $ownerExpression = $builder->eq($this->backRefFieldName, $this->owner); + $expression = $criteria->getWhereExpression(); + $expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression; + + $criteria = clone $criteria; + $criteria->where($expression); + + $persister = $this->em->getUnitOfWork()->getEntityPersister($this->association['targetEntity']); + + return ($this->association['fetch'] === ClassMetadataInfo::FETCH_EXTRA_LAZY) + ? new LazyCriteriaCollection($persister, $criteria) + : new ArrayCollection($persister->loadCriteria($criteria)); + } + + /** + * Retrieves the wrapped Collection instance. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function unwrap() + { + return $this->collection; + } + + /** + * {@inheritdoc} + */ + protected function doInitialize() + { + // Has NEW objects added through add(). Remember them. + $newObjects = array(); + + if ($this->isDirty) { + $newObjects = $this->collection->toArray(); + } + + $this->collection->clear(); + $this->em->getUnitOfWork()->loadCollection($this); + $this->takeSnapshot(); + + // Reattach NEW objects added through add(), if any. + if ($newObjects) { + foreach ($newObjects as $obj) { + $this->collection->add($obj); + } + + $this->isDirty = true; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/AbstractCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/AbstractCollectionPersister.php new file mode 100644 index 00000000000..094895671c5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/AbstractCollectionPersister.php @@ -0,0 +1,99 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\UnitOfWork; + +/** + * Base class for all collection persisters. + * + * @since 2.0 + * @author Roman Borschel + */ +abstract class AbstractCollectionPersister implements CollectionPersister +{ + /** + * @var EntityManagerInterface + */ + protected $em; + + /** + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * @var UnitOfWork + */ + protected $uow; + + /** + * The database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + protected $quoteStrategy; + + /** + * Initializes a new instance of a class derived from AbstractCollectionPersister. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->conn = $em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Check if entity is in a valid state for operations. + * + * @param object $entity + * + * @return bool + */ + protected function isValidEntityState($entity) + { + $entityState = $this->uow->getEntityState($entity, 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 && $this->uow->isScheduledForInsert($entity)) { + return false; + } + + return true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/CollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/CollectionPersister.php new file mode 100644 index 00000000000..36b5706a078 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/CollectionPersister.php @@ -0,0 +1,122 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\PersistentCollection; + +/** + * Collection persister interface + * Define the behavior that should be implemented by all collection persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface CollectionPersister +{ + /** + * Deletes the persistent state represented by the given collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return void + */ + public function delete(PersistentCollection $collection); + + /** + * Updates the given collection, synchronizing its state with the database + * by inserting, updating and deleting individual elements. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return void + */ + public function update(PersistentCollection $collection); + + /** + * Counts the size of this persistent collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return integer + */ + public function count(PersistentCollection $collection); + + /** + * Slices elements. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param integer $offset + * @param integer $length + * + * @return array + */ + public function slice(PersistentCollection $collection, $offset, $length = null); + + /** + * Checks for existence of an element. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * + * @return boolean + */ + public function contains(PersistentCollection $collection, $element); + + /** + * Checks for existence of a key. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $key + * + * @return boolean + */ + public function containsKey(PersistentCollection $collection, $key); + + /** + * Removes an element. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * + * @return mixed + */ + public function removeElement(PersistentCollection $collection, $element); + + /** + * Gets an element by key. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $index + * + * @return mixed + */ + public function get(PersistentCollection $collection, $index); + + /** + * Loads association entities matching the given Criteria object. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php new file mode 100644 index 00000000000..bf9f14e688b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php @@ -0,0 +1,724 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Persisters\SqlExpressionVisitor; +use Doctrine\ORM\Persisters\SqlValueVisitor; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Query; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * Persister for many-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class ManyToManyPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $this->conn->executeUpdate($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection)); + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection); + list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection); + + foreach ($collection->getDeleteDiff() as $element) { + $this->conn->executeUpdate( + $deleteSql, + $this->getDeleteRowSQLParameters($collection, $element), + $deleteTypes + ); + } + + foreach ($collection->getInsertDiff() as $element) { + $this->conn->executeUpdate( + $insertSql, + $this->getInsertRowSQLParameters($collection, $element), + $insertTypes + ); + } + } + + /** + * {@inheritdoc} + */ + public function get(PersistentCollection $collection, $index) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $mappedKey = $mapping['isOwningSide'] + ? $mapping['inversedBy'] + : $mapping['mappedBy']; + + return $persister->load(array($mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1); + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $collection) + { + $conditions = array(); + $params = array(); + $types = array(); + $mapping = $collection->getMapping(); + $id = $this->uow->getEntityIdentifier($collection->getOwner()); + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $association = ( ! $mapping['isOwningSide']) + ? $targetClass->associationMappings[$mapping['mappedBy']] + : $mapping; + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform); + $joinColumns = ( ! $mapping['isOwningSide']) + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform); + $referencedName = $joinColumn['referencedColumnName']; + $conditions[] = 't.' . $columnName . ' = ?'; + $params[] = $id[$sourceClass->getFieldForColumn($referencedName)]; + $types[] = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em); + } + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); + + if ($filterSql) { + $conditions[] = $filterSql; + } + + // If there is a provided criteria, make part of conditions + // @todo Fix this. Current SQL returns something like: + // + /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) { + // A join is needed on the target entity + $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); + $targetJoinSql = ' JOIN ' . $targetTableName . ' te' + . ' ON' . implode(' AND ', $this->getOnConditionSQL($association)); + + // And criteria conditions needs to be added + $persister = $this->uow->getEntityPersister($targetClass->name); + $visitor = new SqlExpressionVisitor($persister, $targetClass); + $conditions[] = $visitor->dispatch($expression); + + $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL; + }*/ + + $sql = 'SELECT COUNT(*)' + . ' FROM ' . $joinTableName . ' t' + . $joinTargetEntitySQL + . ' WHERE ' . implode(' AND ', $conditions); + + return $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * {@inheritDoc} + */ + public function slice(PersistentCollection $collection, $offset, $length = null) + { + $mapping = $collection->getMapping(); + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length); + } + /** + * {@inheritdoc} + */ + public function containsKey(PersistentCollection $collection, $key) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true); + + $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * {@inheritDoc} + */ + public function contains(PersistentCollection $collection, $element) + { + if ( ! $this->isValidEntityState($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true); + + $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * {@inheritDoc} + */ + public function removeElement(PersistentCollection $collection, $element) + { + if ( ! $this->isValidEntityState($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false); + + $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->executeUpdate($sql, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + { + $mapping = $collection->getMapping(); + $owner = $collection->getOwner(); + $ownerMetadata = $this->em->getClassMetadata(get_class($owner)); + $whereClauses = $params = array(); + + foreach ($mapping['relationToSourceKeyColumns'] as $key => $value) { + $whereClauses[] = sprintf('t.%s = ?', $key); + $params[] = $ownerMetadata->getFieldValue($owner, $value); + } + + $parameters = $this->expandCriteriaParameters($criteria); + + foreach ($parameters as $parameter) { + list($name, $value) = $parameter; + $whereClauses[] = sprintf('te.%s = ?', $name); + $params[] = $value; + } + + $mapping = $collection->getMapping(); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $ownerMetadata, $this->platform); + $onConditions = $this->getOnConditionSQL($mapping); + + $rsm = new Query\ResultSetMappingBuilder($this->em); + $rsm->addRootEntityFromClassMetadata($mapping['targetEntity'], 'te'); + + $sql = 'SELECT ' . $rsm->generateSelectClause() + . ' FROM ' . $tableName . ' te' + . ' JOIN ' . $joinTable . ' t ON' + . implode(' AND ', $onConditions) + . ' WHERE ' . implode(' AND ', $whereClauses); + + $stmt = $this->conn->executeQuery($sql, $params); + + return $this + ->em + ->newHydrator(Query::HYDRATE_OBJECT) + ->hydrateAll($stmt, $rsm); + } + + /** + * 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[] ordered tuple: + * - JOIN condition to add to the SQL + * - WHERE condition to add to the SQL + */ + public function getFilterSql($mapping) + { + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); + $filterSql = $this->generateFilterConditionSQL($rootClass, 'te'); + + if ('' === $filterSql) { + return array('', ''); + } + + // A join is needed if there is filtering on the target entity + $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform); + $joinSql = ' JOIN ' . $tableName . ' te' + . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping)); + + return array($joinSql, $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 . ')'; + } + } + + return $filterClauses + ? '(' . implode(' AND ', $filterClauses) . ')' + : ''; + } + + /** + * Generate ON condition + * + * @param array $mapping + * + * @return array + */ + protected function getOnConditionSQL($mapping) + { + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $association = ( ! $mapping['isOwningSide']) + ? $targetClass->associationMappings[$mapping['mappedBy']] + : $mapping; + + $joinColumns = $mapping['isOwningSide'] + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + $conditions = array(); + + foreach ($joinColumns as $joinColumn) { + $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName; + } + + return $conditions; + } + + /** + * {@inheritdoc} + * + * @override + */ + protected function getDeleteSQL(PersistentCollection $collection) + { + $columns = array(); + $mapping = $collection->getMapping(); + $class = $this->em->getClassMetadata(get_class($collection->getOwner())); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'DELETE FROM ' . $joinTable + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + } + + /** + * {@inheritdoc} + * + * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql. + * @override + */ + protected function getDeleteSQLParameters(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + $identifier = $this->uow->getEntityIdentifier($collection->getOwner()); + + // Optimization for single column identifier + if (count($mapping['relationToSourceKeyColumns']) === 1) { + return array(reset($identifier)); + } + + // Composite identifier + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $params = array(); + + foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { + $params[] = isset($sourceClass->fieldNames[$refColumnName]) + ? $identifier[$sourceClass->fieldNames[$refColumnName]] + : $identifier[$sourceClass->getFieldForColumn($columnName)]; + } + + return $params; + } + + /** + * Gets the SQL statement used for deleting a row from the collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array + * of types for bound parameters + */ + protected function getDeleteRowSQL(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + $class = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $columns = array(); + $types = array(); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + } + + return array( + 'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?', + $types, + ); + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete the given + * element from the given collection. + * + * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $element + * + * @return array + */ + protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element) + { + return $this->collectJoinTableColumnParameters($collection, $element); + } + + /** + * Gets the SQL statement used for inserting a row in the collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array + * of types for bound parameters + */ + protected function getInsertRowSQL(PersistentCollection $collection) + { + $columns = array(); + $types = array(); + $mapping = $collection->getMapping(); + $class = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + } + + return array( + 'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) + . ' (' . implode(', ', $columns) . ')' + . ' VALUES' + . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')', + $types, + ); + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $element + * + * @return array + */ + protected function getInsertRowSQLParameters(PersistentCollection $collection, $element) + { + return $this->collectJoinTableColumnParameters($collection, $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 \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * + * @return array + */ + private function collectJoinTableColumnParameters(PersistentCollection $collection, $element) + { + $params = array(); + $mapping = $collection->getMapping(); + $isComposite = count($mapping['joinTableColumns']) > 2; + + $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner()); + $identifier2 = $this->uow->getEntityIdentifier($element); + + if ($isComposite) { + $class1 = $this->em->getClassMetadata(get_class($collection->getOwner())); + $class2 = $collection->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; + } + + /** + * @param \Doctrine\ORM\PersistentCollection $collection + * @param string $key + * @param boolean $addFilters Whether the filter SQL should be included or not. + * + * @return array ordered vector: + * - quoted join table name + * - where clauses to be added for filtering + * - parameters to be bound for filtering + * - types of the parameters to be bound for filtering + */ + private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters) + { + $filterMapping = $collection->getMapping(); + $mapping = $filterMapping; + $indexBy = $mapping['indexBy']; + $id = $this->uow->getEntityIdentifier($collection->getOwner()); + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + if (! $mapping['isOwningSide']) { + $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']); + $mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']]; + $joinColumns = $mapping['joinTable']['joinColumns']; + $sourceRelationMode = 'relationToTargetKeyColumns'; + $targetRelationMode = 'relationToSourceKeyColumns'; + } else { + $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $joinColumns = $mapping['joinTable']['inverseJoinColumns']; + $sourceRelationMode = 'relationToSourceKeyColumns'; + $targetRelationMode = 'relationToTargetKeyColumns'; + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t'; + $whereClauses = array(); + $params = array(); + $types = array(); + + $joinNeeded = ! in_array($indexBy, $targetClass->identifier); + + if ($joinNeeded) { // extra join needed if indexBy is not a @id + $joinConditions = array(); + + foreach ($joinColumns as $joinTableColumn) { + $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName']; + } + + $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); + $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions); + $columnName = $targetClass->getColumnName($indexBy); + + $whereClauses[] = 'tr.' . $columnName . ' = ?'; + $params[] = $key; + $types[] = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em); + } + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + if (isset($mapping[$sourceRelationMode][$joinTableColumn])) { + $column = $mapping[$sourceRelationMode][$joinTableColumn]; + $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; + $params[] = $sourceClass->containsForeignIdentifier + ? $id[$sourceClass->getFieldForColumn($column)] + : $id[$sourceClass->fieldNames[$column]]; + $types[] = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em); + } elseif ( ! $joinNeeded) { + $column = $mapping[$targetRelationMode][$joinTableColumn]; + + $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; + $params[] = $key; + $types[] = PersisterHelper::getTypeOfColumn($column, $targetClass, $this->em); + } + } + + if ($addFilters) { + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + + if ($filterSql) { + $quotedJoinTable .= ' ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params, $types); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * @param boolean $addFilters Whether the filter SQL should be included or not. + * + * @return array ordered vector: + * - quoted join table name + * - where clauses to be added for filtering + * - parameters to be bound for filtering + * - types of the parameters to be bound for filtering + */ + private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters) + { + $filterMapping = $collection->getMapping(); + $mapping = $filterMapping; + + if ( ! $mapping['isOwningSide']) { + $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $sourceId = $this->uow->getEntityIdentifier($element); + $targetId = $this->uow->getEntityIdentifier($collection->getOwner()); + + $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; + } else { + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $sourceId = $this->uow->getEntityIdentifier($collection->getOwner()); + $targetId = $this->uow->getEntityIdentifier($element); + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); + $whereClauses = array(); + $params = array(); + $types = array(); + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; + + if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { + $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn]; + $params[] = $targetId[$targetClass->getFieldForColumn($targetColumn)]; + $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); + + continue; + } + + // relationToSourceKeyColumns + $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn]; + $params[] = $sourceId[$sourceClass->getFieldForColumn($targetColumn)]; + $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em); + } + + if ($addFilters) { + $quotedJoinTable .= ' t'; + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + + if ($filterSql) { + $quotedJoinTable .= ' ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params, $types); + } + + /** + * Expands Criteria Parameters by walking the expressions and grabbing all + * parameters and types from it. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + private function expandCriteriaParameters(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + + if ($expression === null) { + return array(); + } + + $valueVisitor = new SqlValueVisitor(); + + $valueVisitor->dispatch($expression); + + list($values, $types) = $valueVisitor->getParamsAndTypes(); + + return $types; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php new file mode 100644 index 00000000000..c18fdbbb9f1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php @@ -0,0 +1,184 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Proxy\Proxy; +use Doctrine\ORM\PersistentCollection; + +/** + * Persister for one-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class OneToManyPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + // This can never happen. One to many can only be inverse side. + // For owning side one to many, it is required to have a join table, + // then classifying it as a ManyToManyPersister. + return; + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + // This can never happen. One to many can only be inverse side. + // For owning side one to many, it is required to have a join table, + // then classifying it as a ManyToManyPersister. + return; + } + + /** + * {@inheritdoc} + */ + public function get(PersistentCollection $collection, $index) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + return $persister->load( + array( + $mapping['mappedBy'] => $collection->getOwner(), + $mapping['indexBy'] => $index + ), + null, + $mapping, + array(), + null, + 1 + ); + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + $persister = $this->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. + $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + + return $persister->count($criteria); + } + + /** + * {@inheritdoc} + */ + public function slice(PersistentCollection $collection, $offset, $length = null) + { + $mapping = $collection->getMapping(); + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + return $persister->getOneToManyCollection($mapping, $collection->getOwner(), $offset, $length); + } + + /** + * {@inheritdoc} + */ + public function containsKey(PersistentCollection $collection, $key) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + $persister = $this->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. + $criteria = new Criteria(); + + $criteria->andWhere(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + $criteria->andWhere(Criteria::expr()->eq($mapping['indexBy'], $key)); + + return (bool) $persister->count($criteria); + } + + /** + * {@inheritdoc} + */ + public function contains(PersistentCollection $collection, $element) + { + if ( ! $this->isValidEntityState($element)) { + return false; + } + + $mapping = $collection->getMapping(); + $persister = $this->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. + $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + + return $persister->exists($element, $criteria); + } + + /** + * {@inheritdoc} + */ + public function removeElement(PersistentCollection $collection, $element) + { + $mapping = $collection->getMapping(); + + if ( ! $mapping['orphanRemoval']) { + // no-op: this is not the owning side, therefore no operations should be applied + return false; + } + + if ( ! $this->isValidEntityState($element)) { + return false; + } + + return $this + ->uow + ->getEntityPersister($mapping['targetEntity']) + ->delete($element); + } + + /** + * {@inheritdoc} + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + { + throw new \BadMethodCallException("Filtering a collection by Criteria is not supported by this CollectionPersister."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php new file mode 100644 index 00000000000..6b5e5c4f13c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\Mapping\ClassMetadata; +use 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') + { + $tableAlias = $alias == 'r' ? '' : $alias; + $columnName = $class->columnNames[$field]; + $columnAlias = $this->getSQLColumnAlias($columnName); + $sql = $this->getSQLTableAlias($class->name, $tableAlias) . '.' + . $this->quoteStrategy->getColumnName($field, $class, $this->platform); + + $this->currentPersisterContext->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; + } + + /** + * @param string $tableAlias + * @param string $joinColumnName + * @param string $className + * @param string $type + * + * @return string + */ + protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className, $type) + { + $columnAlias = $this->getSQLColumnAlias($joinColumnName); + + $this->currentPersisterContext->rsm->addMetaResult('r', $columnAlias, $joinColumnName, false, $type); + + return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php new file mode 100644 index 00000000000..7ab1d3b1c2c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php @@ -0,0 +1,2066 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\OptimisticLockException; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Persisters\SqlExpressionVisitor; +use Doctrine\ORM\Persisters\SqlValueVisitor; +use Doctrine\ORM\Query; +use Doctrine\ORM\UnitOfWork; +use Doctrine\ORM\Utility\IdentifierFlattener; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * A BasicEntityPersister 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 + * @author Fabio B. Silva + * @author Rob Caiger + * @since 2.0 + */ +class BasicEntityPersister implements EntityPersister +{ + /** + * @var array + */ + static private $comparisonMap = array( + Comparison::EQ => '= %s', + Comparison::IS => '= %s', + Comparison::NEQ => '!= %s', + Comparison::GT => '> %s', + Comparison::GTE => '>= %s', + Comparison::LT => '< %s', + Comparison::LTE => '<= %s', + Comparison::IN => 'IN (%s)', + Comparison::NIN => 'NOT IN (%s)', + Comparison::CONTAINS => 'LIKE %s', + ); + + /** + * 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 EntityManagerInterface + */ + protected $em; + + /** + * Queued inserts. + * + * @var array + */ + protected $queuedInserts = array(); + + /** + * 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 map of quoted column names. + * + * @var array + * + * @see prepareInsertData($entity) + * @see prepareUpdateData($entity) + */ + protected $quotedColumns = 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 quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + protected $quoteStrategy; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * @var CachedPersisterContext + */ + protected $currentPersisterContext; + + /** + * @var CachedPersisterContext + */ + private $limitsHandlingContext; + + /** + * @var CachedPersisterContext + */ + private $noLimitsContext; + + /** + * Initializes a new BasicEntityPersister that uses the given EntityManager + * and persists instances of the class described by the given ClassMetadata descriptor. + * + * @param EntityManagerInterface $em + * @param ClassMetadata $class + */ + public function __construct(EntityManagerInterface $em, ClassMetadata $class) + { + $this->em = $em; + $this->class = $class; + $this->conn = $em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory()); + $this->noLimitsContext = $this->currentPersisterContext = new CachedPersisterContext( + $class, + new Query\ResultSetMapping(), + false + ); + $this->limitsHandlingContext = new CachedPersisterContext( + $class, + new Query\ResultSetMapping(), + true + ); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata() + { + return $this->class; + } + + /** + * {@inheritdoc} + */ + public function getResultSetMapping() + { + return $this->currentPersisterContext->rsm; + } + + /** + * {@inheritdoc} + */ + public function addInsert($entity) + { + $this->queuedInserts[spl_object_hash($entity)] = $entity; + } + + /** + * {@inheritdoc} + */ + public function getInserts() + { + return $this->queuedInserts; + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + if ( ! $this->queuedInserts) { + return array(); + } + + $postInsertIds = array(); + $idGenerator = $this->class->idGenerator; + $isPostInsertId = $idGenerator->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) { + $generatedId = $idGenerator->generate($this->em, $entity); + $id = array( + $this->class->identifier[0] => $generatedId + ); + $postInsertIds[] = array( + 'generatedId' => $generatedId, + 'entity' => $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 array $id + * + * @return void + */ + protected function assignDefaultVersionValue($entity, array $id) + { + $value = $this->fetchVersionValue($this->class, $id); + + $this->class->setFieldValue($entity, $this->class->versionField, $value); + } + + /** + * Fetches the current version value of a versioned entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass + * @param array $id + * + * @return mixed + */ + protected function fetchVersionValue($versionedClass, array $id) + { + $versionField = $versionedClass->versionField; + $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); + $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform); + $columnName = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->platform); + + // FIXME: Order with composite keys might not be correct + $sql = 'SELECT ' . $columnName + . ' FROM ' . $tableName + . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; + + $flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id); + + $value = $this->conn->fetchColumn($sql, array_values($flatId)); + + return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->platform); + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $tableName = $this->class->getTableName(); + $updateData = $this->prepareUpdateData($entity); + + if ( ! isset($updateData[$tableName]) || ! ($data = $updateData[$tableName])) { + return; + } + + $isVersioned = $this->class->isVersioned; + $quotedTableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + $this->updateTable($entity, $quotedTableName, $data, $isVersioned); + + if ($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. + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + */ + protected final function updateTable($entity, $quotedTableName, array $updateData, $versioned = false) + { + $set = array(); + $types = array(); + $params = array(); + + foreach ($updateData as $columnName => $value) { + $placeholder = '?'; + $column = $columnName; + + switch (true) { + case isset($this->class->fieldNames[$columnName]): + $fieldName = $this->class->fieldNames[$columnName]; + $column = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); + + if (isset($this->class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($this->columnTypes[$columnName]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); + } + + break; + + case isset($this->quotedColumns[$columnName]): + $column = $this->quotedColumns[$columnName]; + + break; + } + + $params[] = $value; + $set[] = $column . ' = ' . $placeholder; + $types[] = $this->columnTypes[$columnName]; + } + + $where = array(); + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + + foreach ($this->class->identifier as $idField) { + if ( ! isset($this->class->associationMappings[$idField])) { + + $params[] = $identifier[$idField]; + $types[] = $this->class->fieldMappings[$idField]['type']; + $where[] = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform); + + continue; + } + + $params[] = $identifier[$idField]; + $where[] = $this->class->associationMappings[$idField]['joinColumns'][0]['name']; + $targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']); + + switch (true) { + case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])): + $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + break; + + case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])): + $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + break; + + default: + throw ORMException::unrecognizedField($targetMapping->identifier[0]); + } + + } + + if ($versioned) { + $versionField = $this->class->versionField; + $versionFieldType = $this->class->fieldMappings[$versionField]['type']; + $versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform); + + $where[] = $versionColumn; + $types[] = $this->class->fieldMappings[$versionField]['type']; + $params[] = $this->class->reflFields[$versionField]->getValue($entity); + + switch ($versionFieldType) { + case Type::SMALLINT: + case Type::INTEGER: + case Type::BIGINT: + $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; + break; + + case Type::DATETIME: + $set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; + break; + } + } + + $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) { + continue; + } + + // @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 hierarchy? I hope not! + $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); + $class = $this->class; + $association = $mapping; + $otherColumns = array(); + $otherKeys = array(); + $keys = array(); + + if ( ! $mapping['isOwningSide']) { + $class = $this->em->getClassMetadata($mapping['targetEntity']); + $association = $class->associationMappings[$mapping['mappedBy']]; + } + + $joinColumns = $mapping['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + + if ($selfReferential) { + $otherColumns = (! $mapping['isOwningSide']) + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + } + + foreach ($joinColumns as $joinColumn) { + $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($otherColumns as $joinColumn) { + $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + if (isset($mapping['isOnDeleteCascade'])) { + continue; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); + + $this->conn->delete($joinTableName, array_combine($keys, $identifier)); + + if ($selfReferential) { + $this->conn->delete($joinTableName, array_combine($otherKeys, $identifier)); + } + } + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $class = $this->class; + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $tableName = $this->quoteStrategy->getTableName($class, $this->platform); + $idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform); + $id = array_combine($idColumns, $identifier); + $types = array_map(function ($identifier) use ($class) { + + if (isset($class->fieldMappings[$identifier])) { + return $class->fieldMappings[$identifier]['type']; + } + + $targetMapping = $this->em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']); + + if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) { + return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + } + + if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) { + return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + } + + throw ORMException::unrecognizedField($targetMapping->identifier[0]); + + }, $class->identifier); + + $this->deleteJoinTableRecords($identifier); + + return (bool) $this->conn->delete($tableName, $id, $types); + } + + /** + * 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) + { + $versionField = null; + $result = array(); + $uow = $this->em->getUnitOfWork(); + + if (($versioned = $this->class->isVersioned) != false) { + $versionField = $this->class->versionField; + } + + foreach ($uow->getEntityChangeSet($entity) as $field => $change) { + if (isset($versionField) && $versionField == $field) { + continue; + } + + if (isset($this->class->embeddedClasses[$field])) { + continue; + } + + $newVal = $change[1]; + + if ( ! isset($this->class->associationMappings[$field])) { + $columnName = $this->class->columnNames[$field]; + $this->columnTypes[$columnName] = $this->class->fieldMappings[$field]['type']; + $result[$this->getOwningTable($field)][$columnName] = $newVal; + + continue; + } + + $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; + } + } + + $newValId = null; + + if ($newVal !== null) { + $newValId = $uow->getEntityIdentifier($newVal); + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $owningTable = $this->getOwningTable($field); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $sourceColumn = $joinColumn['name']; + $targetColumn = $joinColumn['referencedColumnName']; + $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + + $this->quotedColumns[$sourceColumn] = $quotedColumn; + $this->columnTypes[$sourceColumn] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); + $result[$owningTable][$sourceColumn] = $newValId + ? $newValId[$targetClass->getFieldForColumn($targetColumn)] + : null; + } + } + + 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); + } + + /** + * {@inheritdoc} + */ + public function getOwningTable($fieldName) + { + return $this->class->getTableName(); + } + + /** + * {@inheritdoc} + */ + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = null, $limit = null, array $orderBy = null) + { + $this->switchPersisterContext(null, $limit); + + $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy); + 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->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + $entities = $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, $hints); + + return $entities ? $entities[0] : null; + } + + /** + * {@inheritdoc} + */ + public function loadById(array $identifier, $entity = null) + { + return $this->load($identifier, $entity); + } + + /** + * {@inheritdoc} + */ + 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); + } + + return $targetEntity; + } + + $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; + } + + /** + * {@inheritdoc} + */ + public function refresh(array $id, $entity, $lockMode = null) + { + $sql = $this->getSelectSQL($id, null, $lockMode); + list($params, $types) = $this->expandParameters($id); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT); + $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, array(Query::HINT_REFRESH => true)); + } + + /** + * {@inheritDoc} + */ + public function count($criteria = array()) + { + $sql = $this->getCountSQL($criteria); + + list($params, $types) = ($criteria instanceof Criteria) + ? $this->expandCriteriaParameters($criteria) + : $this->expandParameters($criteria); + + return $this->conn->executeQuery($sql, $params, $types)->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function loadCriteria(Criteria $criteria) + { + $orderBy = $criteria->getOrderings(); + $limit = $criteria->getMaxResults(); + $offset = $criteria->getFirstResult(); + $query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); + + list($params, $types) = $this->expandCriteriaParameters($criteria); + + $stmt = $this->conn->executeQuery($query, $params, $types); + $hydrator = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * {@inheritdoc} + */ + public function expandCriteriaParameters(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + $sqlParams = array(); + $sqlTypes = array(); + + if ($expression === null) { + return array($sqlParams, $sqlTypes); + } + + $valueVisitor = new SqlValueVisitor(); + + $valueVisitor->dispatch($expression); + + list($params, $types) = $valueVisitor->getParamsAndTypes(); + + foreach ($params as $param) { + $sqlParams = array_merge($sqlParams, $this->getValues($param)); + } + + foreach ($types as $type) { + list ($field, $value) = $type; + $sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class)); + } + + return array($sqlParams, $sqlTypes); + } + + /** + * {@inheritdoc} + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $this->switchPersisterContext($offset, $limit); + + $sql = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + $hydrator = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * {@inheritdoc} + */ + public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $this->switchPersisterContext($offset, $limit); + + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Loads an array of entities from a given DBAL statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * + * @return array + */ + private function loadArrayFromStatement($assoc, $stmt) + { + $rsm = $this->currentPersisterContext->rsm; + $hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } + + return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Hydrates 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) + { + $rsm = $this->currentPersisterContext->rsm; + $hints = array( + UnitOfWork::HINT_DEFEREAGERLOAD => true, + 'collection' => $coll + ); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } + + return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); + } + + /** + * {@inheritdoc} + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return \Doctrine\DBAL\Driver\Statement + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $this->switchPersisterContext($offset, $limit); + + $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + $class = $sourceClass; + $association = $assoc; + $criteria = array(); + $parameters = array(); + + if ( ! $assoc['isOwningSide']) { + $class = $this->em->getClassMetadata($assoc['targetEntity']); + $association = $class->associationMappings[$assoc['mappedBy']]; + } + + $joinColumns = $assoc['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); + + foreach ($joinColumns as $joinColumn) { + + $sourceKeyColumn = $joinColumn['referencedColumnName']; + $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + + switch (true) { + case $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]]; + } + + break; + + case isset($sourceClass->fieldNames[$sourceKeyColumn]): + $field = $sourceClass->fieldNames[$sourceKeyColumn]; + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + break; + + default: + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + + $criteria[$quotedJoinTable . '.' . $quotedKeyColumn] = $value; + $parameters[] = array( + 'value' => $value, + 'field' => $field, + 'class' => $sourceClass, + ); + } + + $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset); + list($params, $types) = $this->expandToManyParameters($parameters); + + return $this->conn->executeQuery($sql, $params, $types); + } + + /** + * {@inheritdoc} + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null) + { + $this->switchPersisterContext($offset, $limit); + + $lockSql = ''; + $joinSql = ''; + $orderBySql = ''; + + if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $joinSql = $this->getSelectManyToManyJoinSQL($assoc); + } + + if (isset($assoc['orderBy'])) { + $orderBy = $assoc['orderBy']; + } + + if ($orderBy) { + $orderBySql = $this->getOrderBySQL($orderBy, $this->getSQLTableAlias($this->class->name)); + } + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria, $assoc); + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $lockSql = ' ' . $this->platform->getReadLockSql(); + break; + + case LockMode::PESSIMISTIC_WRITE: + $lockSql = ' ' . $this->platform->getWriteLockSql(); + break; + } + + $columnList = $this->getSelectColumnsSQL(); + $tableAlias = $this->getSQLTableAlias($this->class->name); + $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias); + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + if ('' !== $filterSql) { + $conditionSql = $conditionSql + ? $conditionSql . ' AND ' . $filterSql + : $filterSql; + } + + $select = 'SELECT ' . $columnList; + $from = ' FROM ' . $tableName . ' '. $tableAlias; + $join = $this->currentPersisterContext->selectJoinSql . $joinSql; + $where = ($conditionSql ? ' WHERE ' . $conditionSql : ''); + $lock = $this->platform->appendLockHint($from, $lockMode); + $query = $select + . $lock + . $join + . $where + . $orderBySql; + + return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; + } + + /** + * {@inheritDoc} + */ + public function getCountSQL($criteria = array()) + { + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + $tableAlias = $this->getSQLTableAlias($this->class->name); + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria); + + $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias); + + if ('' !== $filterSql) { + $conditionSql = $conditionSql + ? $conditionSql . ' AND ' . $filterSql + : $filterSql; + } + + $sql = 'SELECT COUNT(*) ' + . 'FROM ' . $tableName . ' ' . $tableAlias + . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); + + return $sql; + } + + /** + * Gets the ORDER BY SQL snippet for ordered collections. + * + * @param array $orderBy + * @param string $baseTableAlias + * + * @return string + * + * @throws \Doctrine\ORM\ORMException + */ + protected final function getOrderBySQL(array $orderBy, $baseTableAlias) + { + $orderByList = array(); + + foreach ($orderBy as $fieldName => $orientation) { + + $orientation = strtoupper(trim($orientation)); + + if ($orientation != 'ASC' && $orientation != 'DESC') { + throw ORMException::invalidOrientation($this->class->name, $fieldName); + } + + if (isset($this->class->fieldMappings[$fieldName])) { + $tableAlias = isset($this->class->fieldMappings[$fieldName]['inherited']) + ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]['inherited']) + : $baseTableAlias; + + $columnName = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); + $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; + + continue; + } + + if (isset($this->class->associationMappings[$fieldName])) { + + if ( ! $this->class->associationMappings[$fieldName]['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->class->name, $fieldName); + } + + $tableAlias = isset($this->class->associationMappings[$fieldName]['inherited']) + ? $this->getSQLTableAlias($this->class->associationMappings[$fieldName]['inherited']) + : $baseTableAlias; + + foreach ($this->class->associationMappings[$fieldName]['joinColumns'] as $joinColumn) { + $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; + } + + continue; + } + + throw ORMException::unrecognizedField($fieldName); + } + + return ' ORDER BY ' . implode(', ', $orderByList); + } + + /** + * 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. + */ + protected function getSelectColumnsSQL() + { + if ($this->currentPersisterContext->selectColumnListSql !== null) { + return $this->currentPersisterContext->selectColumnListSql; + } + + $columnList = array(); + $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); // r for root + + // Add regular columns to select list + foreach ($this->class->fieldNames as $field) { + $columnList[] = $this->getSelectColumnSQL($field, $this->class); + } + + $this->currentPersisterContext->selectJoinSql = ''; + $eagerAliasCounter = 0; + + foreach ($this->class->associationMappings as $assocField => $assoc) { + $assocColumnSQL = $this->getSelectColumnAssociationSQL($assocField, $assoc, $this->class); + + if ($assocColumnSQL) { + $columnList[] = $assocColumnSQL; + } + + $isAssocToOneInverseSide = $assoc['type'] & ClassMetadata::TO_ONE && ! $assoc['isOwningSide']; + $isAssocFromOneEager = $assoc['type'] !== ClassMetadata::MANY_TO_MANY && $assoc['fetch'] === ClassMetadata::FETCH_EAGER; + + if ( ! ($isAssocFromOneEager || $isAssocToOneInverseSide)) { + continue; + } + + if ((($assoc['type'] & ClassMetadata::TO_MANY) > 0) && $this->currentPersisterContext->handlesLimits) { + continue; + } + + $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->currentPersisterContext->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); + + foreach ($eagerEntity->fieldNames as $field) { + $columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias); + } + + foreach ($eagerEntity->associationMappings as $eagerAssocField => $eagerAssoc) { + $eagerAssocColumnSQL = $this->getSelectColumnAssociationSQL( + $eagerAssocField, $eagerAssoc, $eagerEntity, $assocAlias + ); + + if ($eagerAssocColumnSQL) { + $columnList[] = $eagerAssocColumnSQL; + } + } + + $association = $assoc; + $joinCondition = array(); + + if (isset($assoc['indexBy'])) { + $this->currentPersisterContext->rsm->addIndexBy($assocAlias, $assoc['indexBy']); + } + + if ( ! $assoc['isOwningSide']) { + $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); + $association = $eagerEntity->getAssociationMapping($assoc['mappedBy']); + } + + $joinTableAlias = $this->getSQLTableAlias($eagerEntity->name, $assocAlias); + $joinTableName = $this->quoteStrategy->getTableName($eagerEntity, $this->platform); + + if ($assoc['isOwningSide']) { + $tableAlias = $this->getSQLTableAlias($association['targetEntity'], $assocAlias); + $this->currentPersisterContext->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association['joinColumns']); + + foreach ($association['joinColumns'] as $joinColumn) { + $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity']) + . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol; + } + + // Add filter SQL + if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) { + $joinCondition[] = $filterSql; + } + + } else { + + $this->currentPersisterContext->selectJoinSql .= ' LEFT JOIN'; + + foreach ($association['joinColumns'] as $joinColumn) { + $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + + $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' + . $this->getSQLTableAlias($association['targetEntity']) . '.' . $targetCol; + } + } + + $this->currentPersisterContext->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON '; + $this->currentPersisterContext->selectJoinSql .= implode(' AND ', $joinCondition); + } + + $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); + + return $this->currentPersisterContext->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') + { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) ) { + return ''; + } + + $columnList = array(); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $type = null; + $isIdentifier = isset($assoc['id']) && $assoc['id'] === true; + $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']); + $columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) + . '.' . $quotedColumn . ' AS ' . $resultColumnName; + $type = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + + $this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type); + } + + return implode(', ', $columnList); + } + + /** + * Gets the SQL join fragment used when selecting entities from a + * many-to-many association. + * + * @param array $manyToMany + * + * @return string + */ + protected function getSelectManyToManyJoinSQL(array $manyToMany) + { + $conditions = array(); + $association = $manyToMany; + $sourceTableAlias = $this->getSQLTableAlias($this->class->name); + + if ( ! $manyToMany['isOwningSide']) { + $targetEntity = $this->em->getClassMetadata($manyToMany['targetEntity']); + $association = $targetEntity->associationMappings[$manyToMany['mappedBy']]; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); + $joinColumns = ($manyToMany['isOwningSide']) + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableName . '.' . $quotedSourceColumn; + } + + return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions); + } + + /** + * {@inheritdoc} + */ + public function getInsertSQL() + { + if ($this->insertSql !== null) { + return $this->insertSql; + } + + $columns = $this->getInsertColumnList(); + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + if (empty($columns)) { + $identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform); + $this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn); + + return $this->insertSql; + } + + $values = array(); + $columns = array_unique($columns); + + foreach ($columns as $column) { + $placeholder = '?'; + + if (isset($this->class->fieldNames[$column]) + && isset($this->columnTypes[$this->class->fieldNames[$column]]) + && isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion'])) { + + $type = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); + } + + $values[] = $placeholder; + } + + $columns = implode(', ', $columns); + $values = implode(', ', $values); + + $this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values); + + 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->embeddedClasses[$name])) { + continue; + } + + if (isset($this->class->associationMappings[$name])) { + $assoc = $this->class->associationMappings[$name]; + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + } + } + + continue; + } + + if ($this->class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->class->identifier[0] != $name) { + $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $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 + * + * @return string + */ + protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $root = $alias == 'r' ? '' : $alias ; + $tableAlias = $this->getSQLTableAlias($class->name, $root); + $columnName = $this->quoteStrategy->getColumnName($field, $class, $this->platform); + $sql = $tableAlias . '.' . $columnName; + $columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]); + + $this->currentPersisterContext->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 + * @param string $assocName + * + * @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->currentPersisterContext->sqlTableAliases[$className])) { + return $this->currentPersisterContext->sqlTableAliases[$className]; + } + + $tableAlias = 't' . $this->currentPersisterContext->sqlAliasCounter++; + + $this->currentPersisterContext->sqlTableAliases[$className] = $tableAlias; + + return $tableAlias; + } + + /** + * {@inheritdoc} + */ + public function lock(array $criteria, $lockMode) + { + $lockSql = ''; + $conditionSql = $this->getSelectConditionSQL($criteria); + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $lockSql = $this->platform->getReadLockSql(); + + break; + case LockMode::PESSIMISTIC_WRITE: + + $lockSql = $this->platform->getWriteLockSql(); + break; + } + + $lock = $this->getLockTablesSql($lockMode); + $where = ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' '; + $sql = 'SELECT 1 ' + . $lock + . $where + . $lockSql; + + list($params, $types) = $this->expandParameters($criteria); + + $this->conn->executeQuery($sql, $params, $types); + } + + /** + * Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @param integer $lockMode One of the Doctrine\DBAL\LockMode::* constants. + * + * @return string + */ + protected function getLockTablesSql($lockMode) + { + return $this->platform->appendLockHint( + 'FROM ' + . $this->quoteStrategy->getTableName($this->class, $this->platform) . ' ' + . $this->getSQLTableAlias($this->class->name), + $lockMode + ); + } + + /** + * Gets the Select Where Condition from a Criteria object. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return string + */ + protected function getSelectConditionCriteriaSQL(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + + if ($expression === null) { + return ''; + } + + $visitor = new SqlExpressionVisitor($this, $this->class); + + return $visitor->dispatch($expression); + } + + /** + * {@inheritdoc} + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) + { + $selectedColumns = array(); + $columns = $this->getSelectConditionStatementColumnSQL($field, $assoc); + + if (count($columns) > 1 && $comparison === Comparison::IN) { + /* + * @todo try to support multi-column IN expressions. + * Example: (col1, col2) IN (('val1A', 'val2A'), ('val1B', 'val2B')) + */ + throw ORMException::cantUseInOperatorOnCompositeKeys(); + } + + foreach ($columns as $column) { + $placeholder = '?'; + + if (isset($this->class->fieldMappings[$field]['requireSQLConversion'])) { + $placeholder = Type::getType($this->class->getTypeOfField($field))->convertToDatabaseValueSQL($placeholder, $this->platform); + } + + if (null !== $comparison) { + // special case null value handling + if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && null ===$value) { + $selectedColumns[] = $column . ' IS NULL'; + continue; + } + + if ($comparison === Comparison::NEQ && null === $value) { + $selectedColumns[] = $column . ' IS NOT NULL'; + continue; + } + + $selectedColumns[] = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder); + continue; + } + + if (is_array($value)) { + $in = sprintf('%s IN (%s)', $column, $placeholder); + + if (false !== array_search(null, $value, true)) { + $selectedColumns[] = sprintf('(%s OR %s IS NULL)', $in, $column); + continue; + } + + $selectedColumns[] = $in; + continue; + } + + if (null === $value) { + $selectedColumns[] = sprintf('%s IS NULL', $column); + continue; + } + + $selectedColumns[] = sprintf('%s = %s', $column, $placeholder); + } + + return implode(' AND ', $selectedColumns); + } + + /** + * Builds the left-hand-side of a where condition statement. + * + * @param string $field + * @param array|null $assoc + * + * @return string[] + * + * @throws \Doctrine\ORM\ORMException + */ + private function getSelectConditionStatementColumnSQL($field, $assoc = null) + { + if (isset($this->class->columnNames[$field])) { + $className = (isset($this->class->fieldMappings[$field]['inherited'])) + ? $this->class->fieldMappings[$field]['inherited'] + : $this->class->name; + + return array($this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->class, $this->platform)); + } + + if (isset($this->class->associationMappings[$field])) { + $association = $this->class->associationMappings[$field]; + // Many-To-Many requires join table check for joinColumn + $columns = array(); + $class = $this->class; + + if ($association['type'] === ClassMetadata::MANY_TO_MANY) { + if ( ! $association['isOwningSide']) { + $association = $assoc; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); + $joinColumns = $assoc['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + + foreach ($joinColumns as $joinColumn) { + $columns[] = $joinTableName . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + } else { + + if ( ! $association['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->class->name, $field); + } + + $className = (isset($association['inherited'])) + ? $association['inherited'] + : $this->class->name; + + foreach ($association['joinColumns'] as $joinColumn) { + $columns[] = $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + } + } + return $columns; + } + + 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" + return array($field); + } + + throw ORMException::unrecognizedField($field); + } + + /** + * 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 array|null $assoc + * + * @return string + */ + protected function getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditions = array(); + + foreach ($criteria as $field => $value) { + $conditions[] = $this->getSelectConditionStatementSQL($field, $value, $assoc); + } + + return implode(' AND ', $conditions); + } + + /** + * {@inheritdoc} + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $this->switchPersisterContext($offset, $limit); + + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * {@inheritdoc} + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * Builds 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) + { + $this->switchPersisterContext($offset, $limit); + + $criteria = array(); + $parameters = 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; + $parameters[] = array( + 'value' => $value, + 'field' => $field, + 'class' => $sourceClass, + ); + + continue; + } + + $field = $sourceClass->fieldNames[$sourceKeyColumn]; + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + $criteria[$tableAlias . "." . $targetKeyColumn] = $value; + $parameters[] = array( + 'value' => $value, + 'field' => $field, + 'class' => $sourceClass, + ); + + } + + $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset); + list($params, $types) = $this->expandToManyParameters($parameters); + + return $this->conn->executeQuery($sql, $params, $types); + } + + /** + * {@inheritdoc} + */ + public function expandParameters($criteria) + { + $params = array(); + $types = array(); + + foreach ($criteria as $field => $value) { + if ($value === null) { + continue; // skip null values. + } + + $types = array_merge($types, $this->getTypes($field, $value, $this->class)); + $params = array_merge($params, $this->getValues($value)); + } + + return array($params, $types); + } + + /** + * Expands the parameters from the given criteria and use the correct binding types if found, + * specialized for OneToMany or ManyToMany associations. + * + * @param mixed[][] $criteria an array of arrays containing following: + * - field to which each criterion will be bound + * - value to be bound + * - class to which the field belongs to + * + * + * @return array + */ + private function expandToManyParameters($criteria) + { + $params = array(); + $types = array(); + + foreach ($criteria as $criterion) { + if ($criterion['value'] === null) { + continue; // skip null values. + } + + $types = array_merge($types, $this->getTypes($criterion['field'], $criterion['value'], $criterion['class'])); + $params = array_merge($params, $this->getValues($criterion['value'])); + } + + return array($params, $types); + } + + /** + * Infers field types to be used by parameter type casting. + * + * @param string $field + * @param mixed $value + * + * @return array + * + * @throws \Doctrine\ORM\Query\QueryException + */ + private function getTypes($field, $value, ClassMetadata $class) + { + $types = array(); + + switch (true) { + case (isset($class->fieldMappings[$field])): + $types = array_merge($types, PersisterHelper::getTypeOfField($field, $class, $this->em)); + break; + + case (isset($class->associationMappings[$field])): + $assoc = $class->associationMappings[$field]; + $class = $this->em->getClassMetadata($assoc['targetEntity']); + + if (! $assoc['isOwningSide']) { + $assoc = $class->associationMappings[$assoc['mappedBy']]; + $class = $this->em->getClassMetadata($assoc['targetEntity']); + } + + $columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY + ? $assoc['relationToTargetKeyColumns'] + : $assoc['sourceToTargetKeyColumns']; + + foreach ($columns as $column){ + $types[] = PersisterHelper::getTypeOfColumn($column, $class, $this->em); + } + break; + + default: + $types[] = null; + break; + } + + if (is_array($value)) { + return array_map( + function ($type) { + return Type::getType($type)->getBindingType() + Connection::ARRAY_PARAM_OFFSET; + }, + $types + ); + } + + return $types; + } + + /** + * Retrieves the parameters that identifies a value. + * + * @param mixed $value + * + * @return array + */ + private function getValues($value) + { + if (is_array($value)) { + $newValue = array(); + + foreach ($value as $itemValue) { + $newValue = array_merge($newValue, $this->getValues($itemValue)); + } + + return array($newValue); + } + + if (is_object($value) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + $class = $this->em->getClassMetadata(get_class($value)); + if ($class->isIdentifierComposite) { + $newValue = array(); + + foreach ($class->getIdentifierValues($value) as $innerValue) { + $newValue = array_merge($newValue, $this->getValues($innerValue)); + } + + return $newValue; + } + } + + return array($this->getIndividualValue($value)); + } + + /** + * Retrieves an individual parameter value. + * + * @param mixed $value + * + * @return mixed + */ + private function getIndividualValue($value) + { + if ( ! is_object($value) || ! $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + return $value; + } + + return $this->em->getUnitOfWork()->getSingleIdentifierValue($value); + } + + /** + * {@inheritdoc} + */ + public function exists($entity, Criteria $extraConditions = null) + { + $criteria = $this->class->getIdentifierValues($entity); + + if ( ! $criteria) { + return false; + } + + $alias = $this->getSQLTableAlias($this->class->name); + + $sql = 'SELECT 1 ' + . $this->getLockTablesSql(null) + . ' WHERE ' . $this->getSelectConditionSQL($criteria); + + list($params, $types) = $this->expandParameters($criteria); + + if (null !== $extraConditions) { + $sql .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions); + list($criteriaParams, $criteriaTypes) = $this->expandCriteriaParameters($extraConditions); + + $params = array_merge($params, $criteriaParams); + $types = array_merge($types, $criteriaTypes); + } + + if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) { + $sql .= ' AND ' . $filterSql; + } + + return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * 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'; + } + + /** + * {@inheritdoc} + */ + public function getSQLColumnAlias($columnName) + { + return $this->quoteStrategy->getColumnAlias($columnName, $this->currentPersisterContext->sqlAliasCounter++, $this->platform); + } + + /** + * 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" + } + + /** + * Switches persister context according to current query offset/limits + * + * This is due to the fact that to-many associations cannot be fetch-joined when a limit is involved + * + * @param null|int $offset + * @param null|int $limit + */ + protected function switchPersisterContext($offset, $limit) + { + if (null === $offset && null === $limit) { + $this->currentPersisterContext = $this->noLimitsContext; + + return; + } + + $this->currentPersisterContext = $this->limitsHandlingContext; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php new file mode 100644 index 00000000000..81fde938b3e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * A swappable persister context to use as a container for the current + * generated query/resultSetMapping/type binding information. + * + * This class is a utility class to be used only by the persister API + * + * This object is highly mutable due to performance reasons. Same reasoning + * behind its properties being public. + * + * @author Marco Pivetta + */ +class CachedPersisterContext +{ + /** + * Metadata object that describes the mapping of the mapped entity class. + * + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + public $class; + + /** + * ResultSetMapping that is used for all queries. Is generated lazily once per request. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + public $rsm; + + /** + * 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|null + */ + public $selectColumnListSql; + + /** + * The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one + * associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. + * + * @var string + */ + public $selectJoinSql; + + /** + * Counter for creating unique SQL table and column aliases. + * + * @var integer + */ + public $sqlAliasCounter = 0; + + /** + * Map from class names (FQCN) to the corresponding generated SQL table aliases. + * + * @var array + */ + public $sqlTableAliases = array(); + + /** + * Whether this persistent context is considering limit operations applied to the selection queries + * + * @var bool + */ + public $handlesLimits; + + /** + * @param ClassMetadata $class + * @param ResultSetMapping $rsm + * @param bool $handlesLimits + */ + public function __construct( + ClassMetadata $class, + ResultSetMapping $rsm, + $handlesLimits + ) { + $this->class = $class; + $this->rsm = $rsm; + $this->handlesLimits = (bool) $handlesLimits; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php new file mode 100644 index 00000000000..55887eefa8f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php @@ -0,0 +1,329 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\PersistentCollection; +use Doctrine\Common\Collections\Criteria; + +/** + * Entity persister interface + * Define the behavior that should be implemented by all entity persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface EntityPersister +{ + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getClassMetadata(); + + /** + * Gets the ResultSetMapping used for hydration. + * + * @return \Doctrine\ORM\Query\ResultSetMapping + */ + public function getResultSetMapping(); + + /** + * Get all queued inserts. + * + * @return array + */ + public function getInserts(); + + /** + * @TODO - It should not be here. + * But its necessary since JoinedSubclassPersister#executeInserts invoke the root persister. + * + * Gets the INSERT SQL used by the persister to persist a new entity. + * + * @return string + */ + public function getInsertSQL(); + + /** + * Gets the SELECT SQL to select one or more entities by a set of field criteria. + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * @param array|null $assoc + * @param int|null $lockMode + * @param int|null $limit + * @param int|null $offset + * @param array|null $orderBy + * + * @return string + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null); + + /** + * Get the COUNT SQL to count entities (optionally based on a criteria) + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * @return string + */ + public function getCountSQL($criteria = array()); + + /** + * Expands the parameters from the given criteria and use the correct binding types if found. + * + * @param $criteria + * + * @return array + */ + public function expandParameters($criteria); + + /** + * Expands Criteria Parameters by walking the expressions and grabbing all parameters and types from it. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function expandCriteriaParameters(Criteria $criteria); + + /** + * Gets the SQL WHERE condition for matching a field with a given value. + * + * @param string $field + * @param mixed $value + * @param array|null $assoc + * @param string|null $comparison + * + * @return string + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null); + + /** + * 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. + * + * @return void + */ + public function addInsert($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(); + + /** + * 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. + * + * @param object $entity The entity to update. + * + * @return void + */ + public function update($entity); + + /** + * 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. + * + * @return bool TRUE if the entity got deleted in the database, FALSE otherwise. + */ + public function delete($entity); + + /** + * Count entities (optionally filtered by a criteria) + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * + * @return int + */ + public function count($criteria = array()); + + /** + * 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); + + /** + * Loads an entity by a list of field criteria. + * + * @param array $criteria The criteria by which to load the entity. + * @param object|null $entity The entity to load the data into. If not specified, a new entity is created. + * @param array|null $assoc The association that connects the entity to load to another entity, if any. + * @param array $hints Hints for entity creation. + * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * for loading the entity. + * @param int|null $limit Limit number of results. + * @param array|null $orderBy Criteria to order by. + * + * @return object|null 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 = null, $limit = null, array $orderBy = null); + + /** + * Loads an entity by identifier. + * + * @param array $identifier The entity identifier. + * @param object|null $entity The entity to load the data into. If not specified, a new entity is created. + * + * @return object The loaded and managed entity instance or NULL if the entity can not be found. + * + * @todo Check parameters + */ + public function loadById(array $identifier, $entity = 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. + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()); + + /** + * 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. + * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * for refreshing the managed entity. + * + * @return void + */ + public function refresh(array $id, $entity, $lockMode = null); + + /** + * Loads Entities matching the given Criteria object. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function loadCriteria(Criteria $criteria); + + /** + * Loads a list of entities by a list of field criteria. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null); + + /** + * Gets (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); + + /** + * Loads a collection of entities of a many-to-many association. + * + * @param array $assoc The association mapping of the association being loaded. + * @param object $sourceEntity The entity that owns the collection. + * @param PersistentCollection $collection The collection to fill. + * + * @return array + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection); + + /** + * Loads a collection of entities in a one-to-many association. + * + * @param array $assoc + * @param object $sourceEntity + * @param PersistentCollection $collection The collection to load/fill. + * + * @return array + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection); + + /** + * Locks all rows of this entity matching the given criteria with the specified pessimistic lock mode. + * + * @param array $criteria + * @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants. + * + * @return void + */ + public function lock(array $criteria, $lockMode); + + /** + * Returns an array with (sliced or full list) of elements in the specified collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return array + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null); + + /** + * Checks whether the given managed entity exists in the database. + * + * @param object $entity + * @param Criteria|null $extraConditions + * + * @return boolean TRUE if the entity exists in the database, FALSE otherwise. + */ + public function exists($entity, Criteria $extraConditions = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php new file mode 100644 index 00000000000..b807fbae9ca --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php @@ -0,0 +1,628 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ResultSetMapping; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * 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]; + } + + switch (true) { + case isset($this->class->associationMappings[$fieldName]['inherited']): + $cm = $this->em->getClassMetadata($this->class->associationMappings[$fieldName]['inherited']); + break; + + case isset($this->class->fieldMappings[$fieldName]['inherited']): + $cm = $this->em->getClassMetadata($this->class->fieldMappings[$fieldName]['inherited']); + break; + + default: + $cm = $this->class; + break; + } + + $tableName = $cm->getTableName(); + $quotedTableName = $this->quoteStrategy->getTableName($cm, $this->platform); + + $this->owningTableMap[$fieldName] = $tableName; + $this->quotedTableMap[$tableName] = $quotedTableName; + + return $tableName; + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + if ( ! $this->queuedInserts) { + return array(); + } + + $postInsertIds = array(); + $idGenerator = $this->class->idGenerator; + $isPostInsertId = $idGenerator->isPostInsertGenerator(); + $rootClass = ($this->class->name !== $this->class->rootEntityName) + ? $this->em->getClassMetadata($this->class->rootEntityName) + : $this->class; + + // Prepare statement for the root table + $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) { + $generatedId = $idGenerator->generate($this->em, $entity); + $id = array( + $this->class->identifier[0] => $generatedId + ); + $postInsertIds[] = array( + 'generatedId' => $generatedId, + 'entity' => $entity, + ); + } else { + $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + } + + if ($this->class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + + // 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) { + /** @var \Doctrine\DBAL\Statement $stmt */ + $paramIndex = 1; + $data = isset($insertData[$tableName]) + ? $insertData[$tableName] + : array(); + + 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) { + if (!is_array($id) || !isset($id[$columnName])) { + $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); + } + } + + $stmt->execute(); + } + } + + $rootTableStmt->closeCursor(); + + foreach ($subTableStmts as $stmt) { + $stmt->closeCursor(); + } + + $this->queuedInserts = array(); + + return $postInsertIds; + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $updateData = $this->prepareUpdateData($entity); + + if ( ! $updateData) { + return; + } + + if (($isVersioned = $this->class->isVersioned) === false) { + return; + } + + $versionedClass = $this->getVersionedClassMetadata(); + $versionedTable = $versionedClass->getTableName(); + + foreach ($updateData as $tableName => $data) { + $tableName = $this->quotedTableMap[$tableName]; + $versioned = $isVersioned && $versionedTable === $tableName; + + $this->updateTable($entity, $tableName, $data, $versioned); + } + + // Make sure the table with the version column is updated even if no columns on that + // table were affected. + if ($isVersioned) { + if ( ! isset($updateData[$versionedTable])) { + $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); + $this->updateTable($entity, $tableName, array(), true); + } + + $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $this->assignDefaultVersionValue($entity, $identifiers); + } + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $id = array_combine($this->class->getIdentifierColumnNames(), $identifier); + + $this->deleteJoinTableRecords($identifier); + + // If the database platform supports FKs, just + // delete the row from the root table. Cascades do the rest. + if ($this->platform->supportsForeignKeyConstraints()) { + $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); + $rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform); + + return (bool) $this->conn->delete($rootTable, $id); + } + + // Delete from all tables individually, starting from this class' table up to the root table. + $rootTable = $this->quoteStrategy->getTableName($this->class, $this->platform); + + $affectedRows = $this->conn->delete($rootTable, $id); + + foreach ($this->class->parentClasses as $parentClass) { + $parentMetadata = $this->em->getClassMetadata($parentClass); + $parentTable = $this->quoteStrategy->getTableName($parentMetadata, $this->platform); + + $this->conn->delete($parentTable, $id); + } + + return (bool) $affectedRows; + } + + /** + * {@inheritdoc} + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null) + { + $this->switchPersisterContext($offset, $limit); + + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $joinSql = $this->getJoinSql($baseTableAlias); + + if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $joinSql .= $this->getSelectManyToManyJoinSQL($assoc); + } + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $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))) { + $conditionSql .= $conditionSql + ? ' AND ' . $filterSql + : $filterSql; + } + + $orderBySql = ''; + + if ($assoc !== null && isset($assoc['orderBy'])) { + $orderBy = $assoc['orderBy']; + } + + if ($orderBy) { + $orderBySql = $this->getOrderBySQL($orderBy, $baseTableAlias); + } + + $lockSql = ''; + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + + $lockSql = ' ' . $this->platform->getReadLockSql(); + + break; + + case LockMode::PESSIMISTIC_WRITE: + + $lockSql = ' ' . $this->platform->getWriteLockSql(); + + break; + } + + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + $from = ' FROM ' . $tableName . ' ' . $baseTableAlias; + $where = $conditionSql != '' ? ' WHERE ' . $conditionSql : ''; + $lock = $this->platform->appendLockHint($from, $lockMode); + $columnList = $this->getSelectColumnsSQL(); + $query = 'SELECT ' . $columnList + . $lock + . $joinSql + . $where + . $orderBySql; + + return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; + } + + /** + * {@inheritDoc} + */ + public function getCountSQL($criteria = array()) + { + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $joinSql = $this->getJoinSql($baseTableAlias); + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria); + + $filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName)); + + if ('' !== $filterSql) { + $conditionSql = $conditionSql + ? $conditionSql . ' AND ' . $filterSql + : $filterSql; + } + + $sql = 'SELECT COUNT(*) ' + . 'FROM ' . $tableName . ' ' . $baseTableAlias + . $joinSql + . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getLockTablesSql($lockMode) + { + $joinSql = ''; + $identifierColumns = $this->class->getIdentifierColumnNames(); + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + + // INNER JOIN parent tables + foreach ($this->class->parentClasses as $parentClassName) { + $conditions = array(); + $tableAlias = $this->getSQLTableAlias($parentClassName); + $parentClass = $this->em->getClassMetadata($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + foreach ($identifierColumns as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + return parent::getLockTablesSql($lockMode) . $joinSql; + } + + /** + * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly. + * + * @return string + */ + protected function getSelectColumnsSQL() + { + // Create the column list fragment only once + if ($this->currentPersisterContext->selectColumnListSql !== null) { + return $this->currentPersisterContext->selectColumnListSql; + } + + $columnList = array(); + $discrColumn = $this->class->discriminatorColumn['name']; + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $resultColumnName = $this->platform->getSQLResultCasing($discrColumn); + + $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); + $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumn); + + // Add regular columns + foreach ($this->class->fieldMappings as $fieldName => $mapping) { + $class = isset($mapping['inherited']) + ? $this->em->getClassMetadata($mapping['inherited']) + : $this->class; + + $columnList[] = $this->getSelectColumnSQL($fieldName, $class); + } + + // Add foreign key columns + foreach ($this->class->associationMappings as $mapping) { + if ( ! $mapping['isOwningSide'] || ! ($mapping['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $tableAlias = isset($mapping['inherited']) + ? $this->getSQLTableAlias($mapping['inherited']) + : $baseTableAlias; + + foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($mapping['inherited']) + ? $mapping['inherited'] + : $this->class->name; + + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + $columnList[] = $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + $className, + PersisterHelper::getTypeOfColumn( + $mapping['sourceToTargetKeyColumns'][$srcColumn], + $targetClass, + $this->em + ) + ); + } + } + + // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult). + $tableAlias = ($this->class->rootEntityName == $this->class->name) + ? $baseTableAlias + : $this->getSQLTableAlias($this->class->rootEntityName); + + $columnList[] = $tableAlias . '.' . $discrColumn; + + // sub tables + foreach ($this->class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClassName); + + // 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 $mapping) { + if ( ! $mapping['isOwningSide'] + || ! ($mapping['type'] & ClassMetadata::TO_ONE) + || isset($mapping['inherited'])) { + continue; + } + + foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($mapping['inherited']) + ? $mapping['inherited'] + : $subClass->name; + + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + $columnList[] = $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + $className, + PersisterHelper::getTypeOfColumn( + $mapping['sourceToTargetKeyColumns'][$srcColumn], + $targetClass, + $this->em + ) + ); + } + } + } + + $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); + + return $this->currentPersisterContext->selectColumnListSql; + } + + /** + * {@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) + || isset($this->class->embeddedClasses[$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->quoteStrategy->getColumnName($name, $this->class, $this->platform); + $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; + } + } + + // 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, array $id) + { + $value = $this->fetchVersionValue($this->getVersionedClassMetadata(), $id); + $this->class->setFieldValue($entity, $this->class->versionField, $value); + } + + /** + * @param string $baseTableAlias + * @return string + */ + private function getJoinSql($baseTableAlias) + { + $joinSql = ''; + $identifierColumn = $this->class->getIdentifierColumnNames(); + + // INNER JOIN parent tables + foreach ($this->class->parentClasses as $parentClassName) { + $conditions = array(); + $parentClass = $this->em->getClassMetadata($parentClassName); + $tableAlias = $this->getSQLTableAlias($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + + foreach ($identifierColumn as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + // OUTER JOIN sub tables + foreach ($this->class->subClasses as $subClassName) { + $conditions = array(); + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClassName); + $joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + foreach ($identifierColumn as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + return $joinSql; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php new file mode 100644 index 00000000000..f9e4d557363 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php @@ -0,0 +1,198 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * 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 getSelectColumnsSQL() + { + if ($this->currentPersisterContext->selectColumnListSql !== null) { + return $this->currentPersisterContext->selectColumnListSql; + } + + $columnList[] = parent::getSelectColumnsSQL(); + + $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->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->currentPersisterContext->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'])) { + continue; + } + + $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); + } + + // Foreign key columns + foreach ($subClass->associationMappings as $assoc) { + if ( ! $assoc['isOwningSide'] + || ! ($assoc['type'] & ClassMetadata::TO_ONE) + || isset($assoc['inherited'])) { + continue; + } + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($assoc['inherited']) ? $assoc['inherited'] : $this->class->name; + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + $columnList[] = $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + $className, + PersisterHelper::getTypeOfColumn( + $assoc['sourceToTargetKeyColumns'][$srcColumn], + $targetClass, + $this->em + ) + ); + } + } + } + + $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); + + return $this->currentPersisterContext->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); + + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); + } + + /** + * {@inheritdoc} + */ + protected function getSelectConditionCriteriaSQL(Criteria $criteria) + { + $conditionSql = parent::getSelectConditionCriteriaSQL($criteria); + + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); + } + + /** + * @return string + */ + protected function getSelectConditionDiscriminatorValueSQL() + { + $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]); + } + + $values = implode(', ', $values); + $discColumn = $this->class->discriminatorColumn['name']; + $tableAlias = $this->getSQLTableAlias($this->class->name); + + return $tableAlias . '.' . $discColumn . ' IN (' . $values . ')'; + } + + /** + * {@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 don't 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/PersisterException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php new file mode 100644 index 00000000000..111455e5b86 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php @@ -0,0 +1,20 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\Value; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; + +/** + * Visit Expressions and generate SQL WHERE conditions from them. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class SqlExpressionVisitor extends ExpressionVisitor +{ + /** + * @var \Doctrine\ORM\Persisters\Entity\BasicEntityPersister + */ + private $persister; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * @param \Doctrine\ORM\Persisters\Entity\BasicEntityPersister $persister + * @param \Doctrine\ORM\Mapping\ClassMetadata $classMetadata + */ + public function __construct(BasicEntityPersister $persister, ClassMetadata $classMetadata) + { + $this->persister = $persister; + $this->classMetadata = $classMetadata; + } + + /** + * Converts a comparison expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * + * @return mixed + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + if (isset($this->classMetadata->associationMappings[$field]) && + $value !== null && + ! is_object($value) && + ! in_array($comparison->getOperator(), array(Comparison::IN, Comparison::NIN))) { + + throw PersisterException::matchingAssocationFieldRequiresObject($this->classMetadata->name, $field); + } + + return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator()); + } + + /** + * Converts a composite expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr + * + * @return mixed + * + * @throws \RuntimeException + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return '(' . implode(' AND ', $expressionList) . ')'; + + case CompositeExpression::TYPE_OR: + return '(' . implode(' OR ', $expressionList) . ')'; + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * Converts a value expression into the target query language part. + * + * @param \Doctrine\Common\Collections\Expr\Value $value + * + * @return mixed + */ + public function walkValue(Value $value) + { + return '?'; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php new file mode 100644 index 00000000000..0e680ad078d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\Value; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Extract the values from a criteria/expression + * + * @author Benjamin Eberlei + */ +class SqlValueVisitor extends ExpressionVisitor +{ + /** + * @var array + */ + private $values = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * Converts a comparison expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * + * @return mixed + */ + public function walkComparison(Comparison $comparison) + { + $value = $this->getValueFromComparison($comparison); + $field = $comparison->getField(); + $operator = $comparison->getOperator(); + + if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) { + return; + } else if ($operator === Comparison::NEQ && $value === null) { + return; + } + + $this->values[] = $value; + $this->types[] = array($field, $value); + } + + /** + * Converts a composite expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr + * + * @return mixed + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + foreach ($expr->getExpressionList() as $child) { + $this->dispatch($child); + } + } + + /** + * Converts a value expression into the target query language part. + * + * @param \Doctrine\Common\Collections\Expr\Value $value + * + * @return mixed + */ + public function walkValue(Value $value) + { + return; + } + + /** + * Returns the Parameters and Types necessary for matching the last visited expression. + * + * @return array + */ + public function getParamsAndTypes() + { + return array($this->values, $this->types); + } + + /** + * Returns the value from a Comparison. In case of a CONTAINS comparison, + * the value is wrapped in %-signs, because it will be used in a LIKE clause. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * @return mixed + */ + protected function getValueFromComparison(Comparison $comparison) + { + $value = $comparison->getValue()->getValue(); + + return $comparison->getOperator() == Comparison::CONTAINS + ? "%{$value}%" + : $value; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php new file mode 100644 index 00000000000..d60f7a82177 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Pessimistic Lock Exception + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class PessimisticLockException extends ORMException +{ + /** + * @return PessimisticLockException + */ + public static function lockFailed() + { + return new self("The pessimistic lock failed."); + } +} 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 00000000000..9b2e2cdfa1a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Proxy\Autoloader as BaseAutoloader; + +/** + * @deprecated use \Doctrine\Common\Proxy\Autoloader instead + */ +class Autoloader extends BaseAutoloader +{ +} 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 00000000000..f478d4905c0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Proxy\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/ProxyFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php new file mode 100644 index 00000000000..c6f1cbd3d6a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -0,0 +1,241 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Proxy\AbstractProxyFactory; +use Doctrine\Common\Proxy\Proxy as BaseProxy; +use Doctrine\Common\Proxy\ProxyDefinition; +use Doctrine\Common\Proxy\ProxyGenerator; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Persisters\Entity\EntityPersister; +use Doctrine\ORM\EntityNotFoundException; +use Doctrine\ORM\Utility\IdentifierFlattener; + +/** + * This factory is used to create proxy objects for entities at runtime. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @author Marco Pivetta + * @since 2.0 + */ +class ProxyFactory extends AbstractProxyFactory +{ + /** + * @var EntityManagerInterface The EntityManager this factory is bound to. + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork The UnitOfWork this factory uses to retrieve persisters + */ + private $uow; + + /** + * @var string + */ + private $proxyNs; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param EntityManagerInterface $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|int $autoGenerate The strategy for automatically generating proxy classes. Possible + * values are constants of Doctrine\Common\Proxy\AbstractProxyFactory. + */ + public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER) + { + $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs); + + $proxyGenerator->setPlaceholder('baseProxyInterface', 'Doctrine\ORM\Proxy\Proxy'); + parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate); + + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->proxyNs = $proxyNs; + $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory()); + } + + /** + * {@inheritDoc} + */ + protected function skipClass(ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + return $metadata->isMappedSuperclass || $metadata->getReflectionClass()->isAbstract(); + } + + /** + * {@inheritDoc} + */ + protected function createProxyDefinition($className) + { + $classMetadata = $this->em->getClassMetadata($className); + $entityPersister = $this->uow->getEntityPersister($className); + + return new ProxyDefinition( + ClassUtils::generateProxyClassName($className, $this->proxyNs), + $classMetadata->getIdentifierFieldNames(), + $classMetadata->getReflectionProperties(), + $this->createInitializer($classMetadata, $entityPersister), + $this->createCloner($classMetadata, $entityPersister) + ); + } + + /** + * Creates a closure capable of initializing a proxy + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $entityPersister + * + * @return \Closure + * + * @throws \Doctrine\ORM\EntityNotFoundException + */ + private function createInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister) + { + if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + + $proxy->__setInitializer(null); + $proxy->__setCloner(null); + + if ($proxy->__isInitialized()) { + return; + } + + $properties = $proxy->__getLazyProperties(); + + foreach ($properties as $propertyName => $property) { + if ( ! isset($proxy->$propertyName)) { + $proxy->$propertyName = $properties[$propertyName]; + } + } + + $proxy->__setInitialized(true); + $proxy->__wakeup(); + + $identifier = $classMetadata->getIdentifierValues($proxy); + + if (null === $entityPersister->loadById($identifier, $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + + throw EntityNotFoundException::fromClassNameAndIdentifier( + $classMetadata->getName(), + $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) + ); + } + }; + } + + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + + $proxy->__setInitializer(null); + $proxy->__setCloner(null); + + if ($proxy->__isInitialized()) { + return; + } + + $properties = $proxy->__getLazyProperties(); + + foreach ($properties as $propertyName => $property) { + if (!isset($proxy->$propertyName)) { + $proxy->$propertyName = $properties[$propertyName]; + } + } + + $proxy->__setInitialized(true); + + $identifier = $classMetadata->getIdentifierValues($proxy); + + if (null === $entityPersister->loadById($identifier, $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + + throw EntityNotFoundException::fromClassNameAndIdentifier( + $classMetadata->getName(), + $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) + ); + } + }; + } + + /** + * Creates a closure capable of finalizing state a cloned proxy + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $entityPersister + * + * @return \Closure + * + * @throws \Doctrine\ORM\EntityNotFoundException + */ + private function createCloner(ClassMetadata $classMetadata, EntityPersister $entityPersister) + { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + if ($proxy->__isInitialized()) { + return; + } + + $proxy->__setInitialized(true); + $proxy->__setInitializer(null); + + $class = $entityPersister->getClassMetadata(); + $identifier = $classMetadata->getIdentifierValues($proxy); + $original = $entityPersister->loadById($identifier); + + if (null === $original) { + throw EntityNotFoundException::fromClassNameAndIdentifier( + $classMetadata->getName(), + $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) + ); + } + + foreach ($class->getReflectionClass()->getProperties() as $property) { + if ( ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) { + continue; + } + + $property->setAccessible(true); + $property->setValue($proxy, $property->getValue($original)); + } + }; + } +} 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 00000000000..1911200a933 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php @@ -0,0 +1,735 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\ParserResult; +use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ParameterTypeInferer; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * A Query object represents a DQL query. + * + * @since 1.0 + * @author Guilherme Blanco + * @author Konsta Vesterinen + * @author Roman Borschel + */ +final class Query extends AbstractQuery +{ + /** + * 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'; + + /** + * @var string + */ + const HINT_CACHE_ENABLED = 'doctrine.cache.enabled'; + + /** + * @var string + */ + const HINT_CACHE_EVICT = 'doctrine.cache.evict'; + + /** + * 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'; + + /** + * The current state of this query. + * + * @var integer + */ + private $_state = self::STATE_CLEAN; + + /** + * A snapshot of the parameter types the query was parsed with. + * + * @var array + */ + private $_parsedTypes = array(); + + /** + * Cached DQL query. + * + * @var string + */ + private $_dql = null; + + /** + * The parser result that holds DQL => SQL information. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The first result to return (the "offset"). + * + * @var integer + */ + private $_firstResult = null; + + /** + * The maximum number of results to return (the "limit"). + * + * @var integer + */ + private $_maxResults = null; + + /** + * The cache driver used for caching queries. + * + * @var \Doctrine\Common\Cache\Cache|null + */ + private $_queryCache; + + /** + * Whether or not expire the query cache. + * + * @var boolean + */ + private $_expireQueryCache = false; + + /** + * The query cache lifetime. + * + * @var int + */ + private $_queryCacheTTL; + + /** + * Whether to use a query cache, if available. Defaults to TRUE. + * + * @var boolean + */ + private $_useQueryCache = true; + + /** + * 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(); + } + + /** + * {@inheritdoc} + */ + protected function getResultSetMapping() + { + // parse query or load from cache + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parse()->getResultSetMapping(); + } + + return $this->_resultSetMapping; + } + + /** + * 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() + { + $types = array(); + + foreach ($this->parameters as $parameter) { + /** @var Query\Parameter $parameter */ + $types[$parameter->getName()] = $parameter->getType(); + } + + // Return previous parser result if the query and the filter collection are both clean + if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) { + return $this->_parserResult; + } + + $this->_state = self::STATE_CLEAN; + $this->_parsedTypes = $types; + + // 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 instanceof ParserResult) { + // 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); + } + + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); + } + + // Prepare parameters + $paramMappings = $this->_parserResult->getParameterMappings(); + $paramCount = count($this->parameters); + $mappingCount = count($paramMappings); + + if ($paramCount > $mappingCount) { + throw QueryException::tooManyParameters($mappingCount, $paramCount); + } elseif ($paramCount < $mappingCount) { + throw QueryException::tooFewParameters($mappingCount, $paramCount); + } + + // evict all cache for the entity region + if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) { + $this->evictEntityCacheRegion(); + } + + list($sqlParams, $types) = $this->processParameterMappings($paramMappings); + + return $executor->execute($this->_em->getConnection(), $sqlParams, $types); + } + + /** + * Evict entity cache region + */ + private function evictEntityCacheRegion() + { + $AST = $this->getAST(); + + if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) { + throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.'); + } + + $className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement) + ? $AST->deleteClause->abstractSchemaName + : $AST->updateClause->abstractSchemaName; + + $this->_em->getCache()->evictEntityRegion($className); + } + + /** + * Processes query parameter mappings. + * + * @param array $paramMappings + * + * @return array + * + * @throws Query\QueryException + */ + private function processParameterMappings($paramMappings) + { + $sqlParams = array(); + $types = array(); + + foreach ($this->parameters as $parameter) { + $key = $parameter->getName(); + $value = $parameter->getValue(); + $rsm = $this->getResultSetMapping(); + + if ( ! isset($paramMappings[$key])) { + throw QueryException::unknownParameter($key); + } + + if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) { + $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]); + } + + $value = $this->processParameterValue($value); + $type = ($parameter->getValue() === $value) + ? $parameter->getType() + : ParameterTypeInferer::inferType($value); + + foreach ($paramMappings[$key] as $position) { + $types[$position] = $type; + } + + $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::parameterTypeMismatch(); + } + + 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\Common\Cache\Cache|null $queryCache 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 Query This query instance. + */ + public function useQueryCache($bool) + { + $this->_useQueryCache = $bool; + + return $this; + } + + /** + * Returns the cache driver used for query caching. + * + * @return \Doctrine\Common\Cache\Cache|null 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 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 ArrayCollection|array|null $parameters The query parameters. + * @param integer $hydrationMode The hydration mode to use. + * + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT) + { + $this->setHint(self::HINT_INTERNAL_ITERATION, true); + + return parent::iterate($parameters, $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 + * + * @throws TransactionRequiredException + */ + public function setLockMode($lockMode) + { + if (in_array($lockMode, array(LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE), true)) { + 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|null The current lock mode of this query or NULL if no specific lock mode is set. + */ + public function getLockMode() + { + $lockMode = $this->getHint(self::HINT_LOCK_MODE); + + if (false === $lockMode) { + return null; + } + + return $lockMode; + } + + /** + * Generate a cache id for the query cache - reusing the Result-Cache-Id generator. + * + * @return string + */ + protected function _getQueryCacheId() + { + ksort($this->_hints); + + $platform = $this->getEntityManager() + ->getConnection() + ->getDatabasePlatform() + ->getName(); + + return md5( + $this->getDql() . serialize($this->_hints) . + '&platform=' . $platform . + ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') . + '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults . + '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->_parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT' + ); + } + + /** + * {@inheritdoc} + */ + protected function getHash() + { + return sha1(parent::getHash(). '-'. $this->_firstResult . '-' . $this->_maxResults); + } + + /** + * 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 00000000000..b8f931b45ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +use Doctrine\ORM\Query\QueryException; + +/** + * Base exception class for AST exceptions. + */ +class ASTException extends QueryException +{ + /** + * @param Node $node + * + * @return ASTException + */ + public static function noDispatchForNode($node) + { + return new self("Double-dispatch for node " . get_class($node) . " is not supported."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php new file mode 100644 index 00000000000..0966d902b84 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of AggregateExpression. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AggregateExpression extends Node +{ + /** + * @var string + */ + public $functionName; + + /** + * @var PathExpression|SimpleArithmeticExpression + */ + public $pathExpression; + + /** + * Some aggregate expressions support distinct, eg COUNT. + * + * @var bool + */ + public $isDistinct = false; + + /** + * @param string $functionName + * @param PathExpression|SimpleArithmeticExpression $pathExpression + * @param bool $isDistinct + */ + public function __construct($functionName, $pathExpression, $isDistinct) + { + $this->functionName = $functionName; + $this->pathExpression = $pathExpression; + $this->isDistinct = $isDistinct; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkAggregateExpression($this); + } +} 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 00000000000..b586cba3084 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticExpression extends Node +{ + /** + * @var SimpleArithmeticExpression|null + */ + public $simpleArithmeticExpression; + + /** + * @var Subselect|null + */ + public $subselect; + + /** + * @return bool + */ + public function isSimpleArithmeticExpression() + { + return (bool) $this->simpleArithmeticExpression; + } + + /** + * @return bool + */ + public function isSubselect() + { + return (bool) $this->subselect; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkArithmeticExpression($this); + } +} 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 00000000000..3120466fd86 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticFactor extends Node +{ + /** + * @var mixed + */ + public $arithmeticPrimary; + + /** + * NULL represents no sign, TRUE means positive and FALSE means negative sign. + * + * @var null|boolean + */ + public $sign; + + /** + * @param mixed $arithmeticPrimary + * @param null|bool $sign + */ + public function __construct($arithmeticPrimary, $sign = null) + { + $this->arithmeticPrimary = $arithmeticPrimary; + $this->sign = $sign; + } + + /** + * @return bool + */ + public function isPositiveSigned() + { + return $this->sign === true; + } + + /** + * @return bool + */ + public function isNegativeSigned() + { + return $this->sign === false; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticFactor($this); + } +} 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 00000000000..e08ae7fbb83 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticTerm extends Node +{ + /** + * @var array + */ + public $arithmeticFactors; + + /** + * @param array $arithmeticFactors + */ + public function __construct(array $arithmeticFactors) + { + $this->arithmeticFactors = $arithmeticFactors; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticTerm($this); + } +} 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 00000000000..1e31fd1a5a6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of BetweenExpression. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class BetweenExpression extends Node +{ + /** + * @var ArithmeticExpression + */ + public $expression; + + /** + * @var ArithmeticExpression + */ + public $leftBetweenExpression; + + /** + * @var ArithmeticExpression + */ + public $rightBetweenExpression; + + /** + * @var bool + */ + public $not; + + /** + * @param ArithmeticExpression $expr + * @param ArithmeticExpression $leftExpr + * @param ArithmeticExpression $rightExpr + */ + public function __construct($expr, $leftExpr, $rightExpr) + { + $this->expression = $expr; + $this->leftBetweenExpression = $leftExpr; + $this->rightBetweenExpression = $rightExpr; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..194f9ace55f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * + * @since 2.1 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CoalesceExpression extends Node +{ + /** + * @var array + */ + public $scalarExpressions = array(); + + /** + * @param array $scalarExpressions + */ + public function __construct(array $scalarExpressions) + { + $this->scalarExpressions = $scalarExpressions; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkCoalesceExpression($this); + } +} 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 00000000000..70989a26784 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CollectionMemberExpression extends Node +{ + public $entityExpression; + + /** + * @var PathExpression + */ + public $collectionValuedPathExpression; + + /** + * @var bool + */ + public $not; + + /** + * @param mixed $entityExpr + * @param PathExpression $collValuedPathExpr + */ + public function __construct($entityExpr, $collValuedPathExpr) + { + $this->entityExpression = $entityExpr; + $this->collectionValuedPathExpression = $collValuedPathExpr; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..05583920df4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php @@ -0,0 +1,72 @@ +. + */ + +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) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ComparisonExpression extends Node +{ + /** + * @var Node + */ + public $leftExpression; + + /** + * @var Node + */ + public $rightExpression; + + /** + * @var string + */ + public $operator; + + /** + * @param Node $leftExpr + * @param string $operator + * @param Node $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpression = $leftExpr; + $this->rightExpression = $rightExpr; + $this->operator = $operator; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkComparisonExpression($this); + } +} 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 00000000000..62c7b2bca26 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalExpression extends Node +{ + /** + * @var array + */ + public $conditionalTerms = array(); + + /** + * @param array $conditionalTerms + */ + public function __construct(array $conditionalTerms) + { + $this->conditionalTerms = $conditionalTerms; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalExpression($this); + } +} 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 00000000000..7c89faa420b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalFactor ::= ["NOT"] ConditionalPrimary + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalFactor extends Node +{ + /** + * @var bool + */ + public $not = false; + + /** + * @var ConditionalPrimary + */ + public $conditionalPrimary; + + /** + * @param ConditionalPrimary $conditionalPrimary + */ + public function __construct($conditionalPrimary) + { + $this->conditionalPrimary = $conditionalPrimary; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalFactor($this); + } +} 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 00000000000..1eed41dce67 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalPrimary extends Node +{ + /** + * @var Node|null + */ + public $simpleConditionalExpression; + + /** + * @var ConditionalExpression|null + */ + public $conditionalExpression; + + /** + * @return bool + */ + public function isSimpleConditionalExpression() + { + return (bool) $this->simpleConditionalExpression; + } + + /** + * @return bool + */ + public function isConditionalExpression() + { + return (bool) $this->conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalPrimary($this); + } +} 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 00000000000..bb92db119bf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php @@ -0,0 +1,52 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalTerm extends Node +{ + /** + * @var array + */ + public $conditionalFactors = array(); + + /** + * @param array $conditionalFactors + */ + public function __construct(array $conditionalFactors) + { + $this->conditionalFactors = $conditionalFactors; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalTerm($this); + } +} 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 00000000000..8ca35c6772f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteClause extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @param string $abstractSchemaName + */ + public function __construct($abstractSchemaName) + { + $this->abstractSchemaName = $abstractSchemaName; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..da6859b8678 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteStatement = DeleteClause [WhereClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteStatement extends Node +{ + /** + * @var DeleteClause + */ + public $deleteClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @param DeleteClause $deleteClause + */ + public function __construct($deleteClause) + { + $this->deleteClause = $deleteClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkDeleteStatement($this); + } +} 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 00000000000..bd978af04be --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EmptyCollectionComparisonExpression extends Node +{ + /** + * @var PathExpression + */ + public $expression; + + /** + * @var bool + */ + public $not; + + /** + * @param PathExpression $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..c53a10775e1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ExistsExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Subselect + */ + public $subselect; + + /** + * @param Subselect $subselect + */ + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..b1d8dfe6b8f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration} + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class FromClause extends Node +{ + /** + * @var array + */ + public $identificationVariableDeclarations = array(); + + /** + * @param array $identificationVariableDeclarations + */ + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFromClause($this); + } +} 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 00000000000..8fade4c7a89 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "ABS" "(" SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class AbsFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + 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 00000000000..0e5edd55bf7 --- /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 ")" + * + * + * @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); + } +} 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 00000000000..1d9c2df0454 --- /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 ")" + * + * + * @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); + } +} 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 00000000000..90ca9b35754 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary }* ")" + * + * + * @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 $secondStringPrimary; + + public $concatExpressions = array(); + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + + $args = array(); + + foreach ($this->concatExpressions as $expression) { + $args[] = $sqlWalker->walkStringPrimary($expression); + } + + return call_user_func_array(array($platform,'getConcatExpression'), $args); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstStringPrimary = $parser->StringPrimary(); + $this->concatExpressions[] = $this->firstStringPrimary; + + $parser->match(Lexer::T_COMMA); + + $this->secondStringPrimary = $parser->StringPrimary(); + $this->concatExpressions[] = $this->secondStringPrimary; + + while ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + $this->concatExpressions[] = $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 00000000000..8e8ce1c77b0 --- /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" + * + * + * @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); + } +} 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 00000000000..4bab247c0ff --- /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" + * + * + * @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); + } +} 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 00000000000..8a9b4185fb2 --- /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" + * + * + * @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); + } +} 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 00000000000..312a52c673b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php @@ -0,0 +1,94 @@ +. + */ + +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" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class DateAddFunction extends FunctionNode +{ + public $firstDateExpression = null; + public $intervalExpression = null; + public $unit = null; + + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + case 'second': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddSecondsExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'hour': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + case 'day': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'month': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + default: + throw QueryException::semanticalError( + 'DATE_ADD() only supports units of type second, hour, day and month.' + ); + } + } + + /** + * @override + */ + 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); + } +} 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 00000000000..a33c2d06f33 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; + +/** + * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DateDiffFunction extends FunctionNode +{ + public $date1; + public $date2; + + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression( + $this->date1->dispatch($sqlWalker), + $this->date2->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + 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); + } +} 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 00000000000..d0e890fa793 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\QueryException; + +/** + * "DATE_ADD(date1, interval, unit)" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class DateSubFunction extends DateAddFunction +{ + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + case 'hour': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + case 'day': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'month': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + default: + throw QueryException::semanticalError( + 'DATE_SUB() only supports units of type hour, day and month.' + ); + } + } +} 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 00000000000..2f33c9da34b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\AST\Node; + +/** + * Abstract Function Node. + * + * + * @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 +{ + /** + * @var string + */ + public $name; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * + * @return string + */ + abstract public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker); + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * + * @return string + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFunction($this); + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return void + */ + abstract public function parse(\Doctrine\ORM\Query\Parser $parser); +} 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 00000000000..242a38bcf72 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\QueryException; + +/** + * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class IdentityFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\PathExpression + */ + public $pathExpression; + + /** + * @var string + */ + public $fieldMapping; + + /** + * {@inheritdoc} + */ + public function getSql(SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getEntityManager()->getConnection()->getDatabasePlatform(); + $quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy(); + $dqlAlias = $this->pathExpression->identificationVariable; + $assocField = $this->pathExpression->field; + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $targetEntity = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + $joinColumn = reset($assoc['joinColumns']); + + if ($this->fieldMapping !== null) { + if ( ! isset($targetEntity->fieldMappings[$this->fieldMapping])) { + throw new QueryException(sprintf('Undefined reference field mapping "%s"', $this->fieldMapping)); + } + + $field = $targetEntity->fieldMappings[$this->fieldMapping]; + $joinColumn = null; + + foreach ($assoc['joinColumns'] as $mapping) { + + if($mapping['referencedColumnName'] === $field['columnName']) { + $joinColumn = $mapping; + + break; + } + } + + if ($joinColumn === null) { + throw new QueryException(sprintf('Unable to resolve the reference field mapping "%s"', $this->fieldMapping)); + } + } + + // The table with the relation may be a subclass, so get the table name from the association definition + $tableName = $sqlWalker->getEntityManager()->getClassMetadata($assoc['sourceEntity'])->getTableName(); + + $tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias); + $columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform); + + return $tableAlias . '.' . $columnName; + } + + /** + * {@inheritdoc} + */ + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->pathExpression = $parser->SingleValuedAssociationPathExpression(); + + if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + $parser->match(Lexer::T_STRING); + + $this->fieldMapping = $parser->getLexer()->token['value']; + } + + $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 00000000000..deb3066e2a2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LENGTH" "(" StringPrimary ")" + * + * + * @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 00000000000..cba45bc1cb1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" + * + * + * @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; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression|bool + */ + 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 00000000000..ffc86b6f8da --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOWER" "(" StringPrimary ")" + * + * + * @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 00000000000..cf4cf0c989a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ModFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $firstSimpleArithmeticExpression; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + 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_IDENTIFIER); + $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 00000000000..7970508f17f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -0,0 +1,123 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SIZE" "(" CollectionValuedPathExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SizeFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\PathExpression + */ + 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->getEntityManager()->getConnection()->getDatabasePlatform(); + $quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy(); + $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 .= $quoteStrategy->getTableName($targetClass, $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 . '.' . $quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $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 .= $quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $platform) . ' ' . $joinTableAlias . ' WHERE '; + + $joinColumns = $assoc['isOwningSide'] + ? $joinTable['joinColumns'] + : $joinTable['inverseJoinColumns']; + + $first = true; + + foreach ($joinColumns as $joinColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sourceColumnName = $quoteStrategy->getColumnName( + $class->fieldNames[$joinColumn['referencedColumnName']], $class, $platform + ); + + $sql .= $joinTableAlias . '.' . $joinColumn['name'] + . ' = ' + . $sourceTableAlias . '.' . $sourceColumnName; + } + } + + return '(' . $sql . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $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 00000000000..c9f1038c494 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SQRT" "(" SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SqrtFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $simpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getSqrtExpression( + $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 00000000000..ee80c06acfa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * + * @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; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $firstSimpleArithmeticExpression; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression|null + */ + 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 00000000000..0c1f5d86f21 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class TrimFunction extends FunctionNode +{ + /** + * @var boolean + */ + public $leading; + + /** + * @var boolean + */ + public $trailing; + + /** + * @var boolean + */ + public $both; + + /** + * @var boolean + */ + public $trimChar = false; + + /** + * @var \Doctrine\ORM\Query\AST\Node + */ + public $stringPrimary; + + /** + * {@inheritdoc} + */ + public function getSql(SqlWalker $sqlWalker) + { + $stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary); + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + $trimMode = $this->getTrimMode(); + $trimChar = ($this->trimChar !== false) + ? $sqlWalker->getConnection()->quote($this->trimChar) + : false; + + return $platform->getTrimExpression($stringPrimary, $trimMode, $trimChar); + } + + /** + * {@inheritdoc} + */ + public function parse(Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->parseTrimMode($parser); + + 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); + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return integer + */ + private function getTrimMode() + { + if ($this->leading) { + return AbstractPlatform::TRIM_LEADING; + } + + if ($this->trailing) { + return AbstractPlatform::TRIM_TRAILING; + } + + if ($this->both) { + return AbstractPlatform::TRIM_BOTH; + } + + return AbstractPlatform::TRIM_UNSPECIFIED; + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return void + */ + private function parseTrimMode(Parser $parser) + { + $lexer = $parser->getLexer(); + $value = $lexer->lookahead['value']; + + if (strcasecmp('leading', $value) === 0) { + $parser->match(Lexer::T_LEADING); + + $this->leading = true; + + return; + } + + if (strcasecmp('trailing', $value) === 0) { + $parser->match(Lexer::T_TRAILING); + + $this->trailing = true; + + return; + } + + if (strcasecmp('both', $value) === 0) { + $parser->match(Lexer::T_BOTH); + + $this->both = true; + + return; + } + } +} 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 00000000000..888009a7b31 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "UPPER" "(" StringPrimary ")" + * + * + * @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 00000000000..a74eed599dc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GeneralCaseExpression extends Node +{ + /** + * @var array + */ + public $whenClauses = array(); + + /** + * @var mixed + */ + public $elseScalarExpression = null; + + /** + * @param array $whenClauses + * @param mixed $elseScalarExpression + */ + public function __construct(array $whenClauses, $elseScalarExpression) + { + $this->whenClauses = $whenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGeneralCaseExpression($this); + } +} 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 00000000000..c05fa5863d0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of GroupByClause. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupByClause extends Node +{ + /** + * @var array + */ + public $groupByItems = array(); + + /** + * @param array $groupByItems + */ + public function __construct(array $groupByItems) + { + $this->groupByItems = $groupByItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGroupByClause($this); + } +} 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 00000000000..1d369fff66a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of HavingClause. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class HavingClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $conditionalExpression; + + /** + * @param ConditionalExpression $conditionalExpression + */ + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkHavingClause($this); + } +} 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 00000000000..a8f7f6d350e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IdentificationVariableDeclaration extends Node +{ + /** + * @var RangeVariableDeclaration|null + */ + public $rangeVariableDeclaration = null; + + /** + * @var IndexBy|null + */ + public $indexBy = null; + + /** + * @var array + */ + public $joins = array(); + + /** + * @param RangeVariableDeclaration|null $rangeVariableDecl + * @param IndexBy|null $indexBy + * @param array $joins + */ + public function __construct($rangeVariableDecl, $indexBy, array $joins) + { + $this->rangeVariableDeclaration = $rangeVariableDecl; + $this->indexBy = $indexBy; + $this->joins = $joins; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIdentificationVariableDeclaration($this); + } +} 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 00000000000..9d0a8b54ee8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php @@ -0,0 +1,67 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var ArithmeticExpression + */ + public $expression; + + /** + * @var array + */ + public $literals = array(); + + /** + * @var Subselect|null + */ + public $subselect; + + /** + * @param ArithmeticExpression $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..c7874b70a35 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IndexBy ::= "INDEX" "BY" SimpleStateFieldPathExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IndexBy extends Node +{ + /** + * @var PathExpression + */ + public $simpleStateFieldPathExpression = null; + + /** + * @param PathExpression $simpleStateFieldPathExpression + */ + public function __construct($simpleStateFieldPathExpression) + { + $this->simpleStateFieldPathExpression = $simpleStateFieldPathExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIndexBy($this); + } +} 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 00000000000..cf50140be9b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of InputParameter. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InputParameter extends Node +{ + /** + * @var bool + */ + public $isNamed; + + /** + * @var string + */ + public $name; + + /** + * @param string $value + * + * @throws \Doctrine\ORM\Query\QueryException + */ + 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; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkInputParameter($this); + } +} 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 00000000000..c1fd65b8e6b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + * InstanceOfParameter ::= AbstractSchemaName | InputParameter + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InstanceOfExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var string + */ + public $identificationVariable; + + /** + * @var array + */ + public $value; + + /** + * @param string $identVariable + */ + public function __construct($identVariable) + { + $this->identificationVariable = $identVariable; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..5c203aa0b47 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression + * ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression] + * + * @link www.doctrine-project.org + * @since 2.0 + * @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; + + /** + * @var int + */ + public $joinType = self::JOIN_TYPE_INNER; + + /** + * @var Node|null + */ + public $joinAssociationDeclaration = null; + + /** + * @var ConditionalExpression|null + */ + public $conditionalExpression = null; + + /** + * @param int $joinType + * @param Node $joinAssociationDeclaration + */ + public function __construct($joinType, $joinAssociationDeclaration) + { + $this->joinType = $joinType; + $this->joinAssociationDeclaration = $joinAssociationDeclaration; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoin($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php new file mode 100644 index 00000000000..a33900a6d65 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class JoinAssociationDeclaration extends Node +{ + /** + * @var JoinAssociationPathExpression + */ + public $joinAssociationPathExpression; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var IndexBy|null + */ + public $indexBy; + + /** + * @param JoinAssociationPathExpression $joinAssociationPathExpression + * @param string $aliasIdentificationVariable + * @param IndexBy|null $indexBy + */ + public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy) + { + $this->joinAssociationPathExpression = $joinAssociationPathExpression; + $this->aliasIdentificationVariable = $aliasIdentificationVariable; + $this->indexBy = $indexBy; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoinAssociationDeclaration($this); + } +} 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 00000000000..946bbb15b7d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationPathExpression ::= IdentificationVariable "." (SingleValuedAssociationField | CollectionValuedAssociationField) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class JoinAssociationPathExpression extends Node +{ + /** + * @var string + */ + public $identificationVariable; + + /** + * @var string + */ + public $associationField; + + /** + * @param string $identificationVariable + * @param string $associationField + */ + public function __construct($identificationVariable, $associationField) + { + $this->identificationVariable = $identificationVariable; + $this->associationField = $associationField; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php new file mode 100644 index 00000000000..7e374149f2d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.3 + * @author Alexander + */ +class JoinClassPathExpression extends Node +{ + /** + * @var mixed + */ + public $abstractSchemaName; + + /** + * @var mixed + */ + public $aliasIdentificationVariable; + + /** + * @param mixed $abstractSchemaName + * @param mixed $aliasIdentificationVar + */ + public function __construct($abstractSchemaName, $aliasIdentificationVar) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkJoinPathExpression($this); + } +} 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 00000000000..89aa83ad8cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinVariableDeclaration ::= Join [IndexBy] + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.5 + * @author Guilherme Blanco + */ +class JoinVariableDeclaration extends Node +{ + /** + * @var Join + */ + public $join; + + /** + * @var IndexBy|null + */ + public $indexBy; + + /** + * Constructor. + * + * @param Join $join + * @param IndexBy|null $indexBy + */ + public function __construct($join, $indexBy) + { + $this->join = $join; + $this->indexBy = $indexBy; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkJoinVariableDeclaration($this); + } +} 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 00000000000..e320c51c882 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php @@ -0,0 +1,71 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * LikeExpression ::= StringExpression ["NOT"] "LIKE" string ["ESCAPE" char] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class LikeExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Node + */ + public $stringExpression; + + /** + * @var InputParameter + */ + public $stringPattern; + + /** + * @var Literal|null + */ + public $escapeChar; + + /** + * @param Node $stringExpression + * @param InputParameter $stringPattern + * @param Literal|null $escapeChar + */ + public function __construct($stringExpression, $stringPattern, $escapeChar = null) + { + $this->stringExpression = $stringExpression; + $this->stringPattern = $stringPattern; + $this->escapeChar = $escapeChar; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkLikeExpression($this); + } +} 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 00000000000..43d71add08f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class Literal extends Node +{ + const STRING = 1; + const BOOLEAN = 2; + const NUMERIC = 3; + + /** + * @var int + */ + public $type; + + /** + * @var mixed + */ + public $value; + + /** + * @param int $type + * @param mixed $value + */ + public function __construct($type, $value) + { + $this->type = $type; + $this->value = $value; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkLiteral($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php new file mode 100644 index 00000000000..65b94ac63cf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Fabio B. Silva + */ +class NewObjectExpression extends Node +{ + /** + * @var string + */ + public $className; + + /** + * @var array + */ + public $args; + + /** + * @param string $className + * @param array $args + */ + public function __construct($className, array $args) + { + $this->className = $className; + $this->args = $args; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNewObject($this); + } +} 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 00000000000..a257dc2d79c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Abstract class of an AST node. + * + * @link www.doctrine-project.org + * @since 2.0 + * @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 \Doctrine\ORM\Query\SqlWalker $walker + * + * @return string + * + * @throws ASTException + */ + 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); + } + + /** + * @param object $obj + * + * @return string + */ + 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; + } +} 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 00000000000..84a199784cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullComparisonExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Node + */ + public $expression; + + /** + * @param Node $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..e33bc72b15e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @since 2.1 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullIfExpression extends Node +{ + /** + * @var mixed + */ + public $firstExpression; + + /** + * @var mixed + */ + public $secondExpression; + + /** + * @param mixed $firstExpression + * @param mixed $secondExpression + */ + public function __construct($firstExpression, $secondExpression) + { + $this->firstExpression = $firstExpression; + $this->secondExpression = $secondExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNullIfExpression($this); + } +} 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 00000000000..75d16c7319d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByClause extends Node +{ + /** + * @var array + */ + public $orderByItems = array(); + + /** + * @param array $orderByItems + */ + public function __construct(array $orderByItems) + { + $this->orderByItems = $orderByItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByClause($this); + } +} 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 00000000000..bf3288a7b9a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByItem ::= (ResultVariable | StateFieldPathExpression) ["ASC" | "DESC"] + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByItem extends Node +{ + /** + * @var mixed + */ + public $expression; + + /** + * @var string + */ + public $type; + + /** + * @param mixed $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * @return bool + */ + public function isAsc() + { + return strtoupper($this->type) == 'ASC'; + } + + /** + * @return bool + */ + public function isDesc() + { + return strtoupper($this->type) == 'DESC'; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByItem($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php new file mode 100644 index 00000000000..f16db0eb74d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ParenthesisExpression ::= "(" ArithmeticPrimary ")" + * + * @author Fabio B. Silva + * @since 2.4 + */ +class ParenthesisExpression extends Node +{ + /** + * @var \Doctrine\ORM\Query\AST\Node + */ + public $expression; + + /** + * @param \Doctrine\ORM\Query\AST\Node $expression + */ + public function __construct(Node $expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkParenthesisExpression($this); + } +} \ No newline at end of file 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 00000000000..e4ffe79b2aa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class PartialObjectExpression extends Node +{ + /** + * @var string + */ + public $identificationVariable; + + /** + * @var array + */ + public $partialFieldSet; + + /** + * @param string $identificationVariable + * @param array $partialFieldSet + */ + public function __construct($identificationVariable, array $partialFieldSet) + { + $this->identificationVariable = $identificationVariable; + $this->partialFieldSet = $partialFieldSet; + } +} 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 00000000000..37674b6fd47 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php @@ -0,0 +1,81 @@ +. + */ + +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; + + /** + * @var int + */ + public $type; + + /** + * @var int + */ + public $expectedType; + + /** + * @var string + */ + public $identificationVariable; + + /** + * @var string|null + */ + public $field; + + /** + * @param int $expectedType + * @param string $identificationVariable + * @param string|null $field + */ + public function __construct($expectedType, $identificationVariable, $field = null) + { + $this->expectedType = $expectedType; + $this->identificationVariable = $identificationVariable; + $this->field = $field; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..15be9523471 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php @@ -0,0 +1,81 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QuantifiedExpression extends Node +{ + /** + * @var string + */ + public $type; + + /** + * @var Subselect + */ + public $subselect; + + /** + * @param Subselect $subselect + */ + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + /** + * @return bool + */ + public function isAll() + { + return strtoupper($this->type) == 'ALL'; + } + + /** + * @return bool + */ + public function isAny() + { + return strtoupper($this->type) == 'ANY'; + } + + /** + * @return bool + */ + public function isSome() + { + return strtoupper($this->type) == 'SOME'; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..0ca5274d19e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RangeVariableDeclaration extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var boolean + */ + public $isRoot; + + /** + * @param string $abstractSchemaName + * @param string $aliasIdentificationVar + * @param boolean $isRoot + */ + public function __construct($abstractSchemaName, $aliasIdentificationVar, $isRoot = true) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + $this->isRoot = $isRoot; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkRangeVariableDeclaration($this); + } +} 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 00000000000..1df143677be --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectClause = "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectClause extends Node +{ + /** + * @var bool + */ + public $isDistinct; + + /** + * @var array + */ + public $selectExpressions = array(); + + /** + * @param array $selectExpressions + * @param bool $isDistinct + */ + public function __construct(array $selectExpressions, $isDistinct) + { + $this->isDistinct = $isDistinct; + $this->selectExpressions = $selectExpressions; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectClause($this); + } +} 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 00000000000..4187013994a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | + * (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectExpression extends Node +{ + /** + * @var mixed + */ + public $expression; + + /** + * @var string|null + */ + public $fieldIdentificationVariable; + + /** + * @var bool + */ + public $hiddenAliasResultVariable; + + /** + * @param mixed $expression + * @param string|null $fieldIdentificationVariable + * @param bool $hiddenAliasResultVariable + */ + public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false) + { + $this->expression = $expression; + $this->fieldIdentificationVariable = $fieldIdentificationVariable; + $this->hiddenAliasResultVariable = $hiddenAliasResultVariable; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectExpression($this); + } +} 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 00000000000..d84f7258a9f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectStatement extends Node +{ + /** + * @var SelectClause + */ + public $selectClause; + + /** + * @var FromClause + */ + public $fromClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @var GroupByClause|null + */ + public $groupByClause; + + /** + * @var HavingClause|null + */ + public $havingClause; + + /** + * @var OrderByClause|null + */ + public $orderByClause; + + /** + * @param SelectClause $selectClause + * @param FromClause $fromClause + */ + public function __construct($selectClause, $fromClause) + { + $this->selectClause = $selectClause; + $this->fromClause = $fromClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectStatement($this); + } +} 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 00000000000..9bd485045f3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleArithmeticExpression extends Node +{ + /** + * @var array + */ + public $arithmeticTerms = array(); + + /** + * @param array $arithmeticTerms + */ + public function __construct(array $arithmeticTerms) + { + $this->arithmeticTerms = $arithmeticTerms; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleArithmeticExpression($this); + } +} 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 00000000000..5272f3962a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleCaseExpression extends Node +{ + /** + * @var PathExpression + */ + public $caseOperand = null; + + /** + * @var array + */ + public $simpleWhenClauses = array(); + + /** + * @var mixed + */ + public $elseScalarExpression = null; + + /** + * @param PathExpression $caseOperand + * @param array $simpleWhenClauses + * @param mixed $elseScalarExpression + */ + public function __construct($caseOperand, array $simpleWhenClauses, $elseScalarExpression) + { + $this->caseOperand = $caseOperand; + $this->simpleWhenClauses = $simpleWhenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleCaseExpression($this); + } +} 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 00000000000..92361da45b7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectClause extends Node +{ + /** + * @var bool + */ + public $isDistinct = false; + + /** + * @var SimpleSelectExpression + */ + public $simpleSelectExpression; + + /** + * @param SimpleSelectExpression $simpleSelectExpression + * @param bool $isDistinct + */ + public function __construct($simpleSelectExpression, $isDistinct) + { + $this->simpleSelectExpression = $simpleSelectExpression; + $this->isDistinct = $isDistinct; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectClause($this); + } +} 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 00000000000..e556835ed61 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable + * | (AggregateExpression [["AS"] FieldAliasIdentificationVariable]) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectExpression extends Node +{ + /** + * @var Node + */ + public $expression; + + /** + * @var string + */ + public $fieldIdentificationVariable; + + /** + * @param Node $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectExpression($this); + } +} 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 00000000000..4f60881d42a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleWhenClause extends Node +{ + /** + * @var mixed + */ + public $caseScalarExpression = null; + + /** + * @var mixed + */ + public $thenScalarExpression = null; + + /** + * @param mixed $caseScalarExpression + * @param mixed $thenScalarExpression + */ + public function __construct($caseScalarExpression, $thenScalarExpression) + { + $this->caseScalarExpression = $caseScalarExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} 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 00000000000..ce08266f083 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Subselect extends Node +{ + /** + * @var SimpleSelectClause + */ + public $simpleSelectClause; + + /** + * @var SubselectFromClause + */ + public $subselectFromClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @var GroupByClause|null + */ + public $groupByClause; + + /** + * @var HavingClause|null + */ + public $havingClause; + + /** + * @var OrderByClause|null + */ + public $orderByClause; + + /** + * @param SimpleSelectClause $simpleSelectClause + * @param SubselectFromClause $subselectFromClause + */ + public function __construct($simpleSelectClause, $subselectFromClause) + { + $this->simpleSelectClause = $simpleSelectClause; + $this->subselectFromClause = $subselectFromClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselect($this); + } +} 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 00000000000..8d009fcfdc1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SubselectFromClause extends Node +{ + /** + * @var array + */ + public $identificationVariableDeclarations = array(); + + /** + * @param array $identificationVariableDeclarations + */ + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselectFromClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectIdentificationVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectIdentificationVariableDeclaration.php new file mode 100644 index 00000000000..8fbd9e2105d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectIdentificationVariableDeclaration.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SubselectIdentificationVariableDeclaration ::= AssociationPathExpression ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + */ +class SubselectIdentificationVariableDeclaration +{ + /** + * @var PathExpression + */ + public $associationPathExpression; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * Constructor. + * + * @param PathExpression $associationPathExpression + * @param string $aliasIdentificationVariable + */ + public function __construct($associationPathExpression, $aliasIdentificationVariable) + { + $this->associationPathExpression = $associationPathExpression; + $this->aliasIdentificationVariable = $aliasIdentificationVariable; + } +} 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 00000000000..430ed14eb93 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateClause extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var array + */ + public $updateItems = array(); + + /** + * @param string $abstractSchemaName + * @param array $updateItems + */ + public function __construct($abstractSchemaName, array $updateItems) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->updateItems = $updateItems; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..f1a288cae21 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue + * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | + * EnumPrimary | SimpleEntityExpression | "NULL" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateItem extends Node +{ + /** + * @var PathExpression + */ + public $pathExpression; + + /** + * @var InputParameter|ArithmeticExpression|null + */ + public $newValue; + + /** + * @param PathExpression $pathExpression + * @param InputParameter|ArithmeticExpression|null $newValue + */ + public function __construct($pathExpression, $newValue) + { + $this->pathExpression = $pathExpression; + $this->newValue = $newValue; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..c578efef487 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateStatement = UpdateClause [WhereClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateStatement extends Node +{ + /** + * @var UpdateClause + */ + public $updateClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @param UpdateClause $updateClause + */ + public function __construct($updateClause) + { + $this->updateClause = $updateClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateStatement($this); + } +} 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 00000000000..01c0330f48c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhenClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $caseConditionExpression = null; + + /** + * @var mixed + */ + public $thenScalarExpression = null; + + /** + * @param ConditionalExpression $caseConditionExpression + * @param mixed $thenScalarExpression + */ + public function __construct($caseConditionExpression, $thenScalarExpression) + { + $this->caseConditionExpression = $caseConditionExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} 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 00000000000..e6597752fff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhereClause ::= "WHERE" ConditionalExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhereClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $conditionalExpression; + + /** + * @param ConditionalExpression $conditionalExpression + */ + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhereClause($this); + } +} 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 00000000000..9be35df187d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -0,0 +1,76 @@ +. + */ + +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/mit-license.php MIT + * @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; + } + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $qcp + * + * @return void + */ + public function setQueryCacheProfile(QueryCacheProfile $qcp) + { + $this->queryCacheProfile = $qcp; + } + + /** + * Executes all sql statements. + * + * @param 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); +} 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 00000000000..4e5303ce7b2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use 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/mit-license.php MIT + * @link http://www.doctrine-project.org + * @since 2.0 + */ +class MultiTableDeleteExecutor extends AbstractSqlExecutor +{ + /** + * @var string + */ + private $_createTempTableSql; + + /** + * @var string + */ + private $_dropTempTableSql; + + /** + * @var string + */ + private $_insertSql; + + /** + * Initializes a new MultiTableDeleteExecutor. + * + * Internal note: Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + * + * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + + $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 = $quoteStrategy->getTableName($em->getClassMetadata($className), $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); + + try { + // Insert identifiers + $numDeleted = $conn->executeUpdate($this->_insertSql, $params, $types); + + // Execute DELETE statements + foreach ($this->_sqlStatements as $sql) { + $conn->executeUpdate($sql); + } + } catch (\Exception $exception) { + // FAILURE! Drop temporary table to avoid possible collisions + $conn->executeUpdate($this->_dropTempTableSql); + + // Re-throw exception + throw $exception; + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numDeleted; + } +} 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 00000000000..54653fc555c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -0,0 +1,206 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +use Doctrine\ORM\Query\ParameterTypeInferer; +use 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 +{ + /** + * @var string + */ + private $_createTempTableSql; + + /** + * @var string + */ + private $_dropTempTableSql; + + /** + * @var string + */ + private $_insertSql; + + /** + * @var array + */ + private $_sqlParameters = array(); + + /** + * @var int + */ + private $_numParametersInUpdateClause = 0; + + /** + * Initializes a new MultiTableUpdateExecutor. + * + * Internal note: Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + * + * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + + $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 ' . $quoteStrategy->getTableName($class, $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); + + if ($newValue instanceof AST\InputParameter) { + $this->_sqlParameters[$i][] = $newValue->name; + + ++$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); + + try { + // 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 + foreach ($this->_sqlStatements as $key => $statement) { + $paramValues = array(); + $paramTypes = array(); + + if (isset($this->_sqlParameters[$key])) { + foreach ($this->_sqlParameters[$key] as $parameterKey => $parameterName) { + $paramValues[] = $params[$parameterKey]; + $paramTypes[] = isset($types[$parameterKey]) ? $types[$parameterKey] : ParameterTypeInferer::inferType($params[$parameterKey]); + } + } + + $conn->executeUpdate($statement, $paramValues, $paramTypes); + } + } catch (\Exception $exception) { + // FAILURE! Drop temporary table to avoid possible collisions + $conn->executeUpdate($this->_dropTempTableSql); + + // Re-throw exception + throw $exception; + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numUpdated; + } +} 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 00000000000..91827ab1adc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\SqlWalker; + +/** + * Executor that executes the SQL statement for simple DQL SELECT statements. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +class SingleSelectExecutor extends AbstractSqlExecutor +{ + /** + * @param \Doctrine\ORM\Query\AST\SelectStatement $AST + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + */ + 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 00000000000..e0183dd6879 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use 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/mit-license.php MIT + * @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 +{ + /** + * @param \Doctrine\ORM\Query\AST\Node $AST + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + */ + 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); + } +} 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 00000000000..b6997d8cb83 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php @@ -0,0 +1,673 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * This class is used to generate DQL expressions via a set of PHP static functions. + * + * @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 to 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 to 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 mixed $expr + * + * @return Expr\OrderBy + */ + public function asc($expr) + { + return new Expr\OrderBy($expr, 'ASC'); + } + + /** + * Creates a DESCending order expression. + * + * @param mixed $expr + * + * @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.percentAnnualSalaryIncrease + * $q->expr()->prod('u.salary', 'u.percentAnnualSalaryIncrease') + * + * @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 NOT 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 notLike($x, $y) + { + return new Expr\Comparison($x, 'NOT LIKE', $y); + } + + /** + * Creates a CONCAT() function expression with the given arguments. + * + * @param mixed $x First argument to be used in CONCAT() function. + * @param mixed $y 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 int $from Initial offset to start cropping string. May accept negative values. + * @param int|null $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); + } + + /** + * Creates an instance of MEMBER OF function, with the given arguments. + * + * @param string $x Value to be checked + * @param string $y Value to be checked against + * + * @return Expr\Comparison + */ + public function isMemberOf($x, $y) + { + return new Expr\Comparison($x, 'MEMBER OF', $y); + } + + /** + * Creates an instance of INSTANCE OF function, with the given arguments. + * + * @param string $x Value to be checked + * @param string $y Value to be checked against + * + * @return Expr\Comparison + */ + public function isInstanceOf($x, $y) + { + return new Expr\Comparison($x, 'INSTANCE OF', $y); + } +} 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 00000000000..432f714f6d9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Andx extends Composite +{ + /** + * @var string + */ + protected $separator = ' AND '; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Orx', + 'Doctrine\ORM\Query\Expr\Andx', + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} 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 00000000000..6eac70a27c6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Abstract base Expr class for building DQL parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Base +{ + /** + * @var string + */ + protected $preSeparator = '('; + + /** + * @var string + */ + protected $separator = ', '; + + /** + * @var string + */ + protected $postSeparator = ')'; + + /** + * @var array + */ + protected $allowedClasses = array(); + + /** + * @var array + */ + protected $parts = array(); + + /** + * @param array $args + */ + public function __construct($args = array()) + { + $this->addMultiple($args); + } + + /** + * @param array $args + * + * @return Base + */ + public function addMultiple($args = array()) + { + foreach ((array) $args as $arg) { + $this->add($arg); + } + + return $this; + } + + /** + * @param mixed $arg + * + * @return Base + * + * @throws \InvalidArgumentException + */ + 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; + } + + /** + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * @return string + */ + 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 00000000000..4103dcea9eb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL comparison expressions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Comparison +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * @var mixed + */ + protected $leftExpr; + + /** + * @var string + */ + protected $operator; + + /** + * @var mixed + */ + protected $rightExpr; + + /** + * Creates a comparison expression with the given arguments. + * + * @param mixed $leftExpr + * @param string $operator + * @param mixed $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpr = $leftExpr; + $this->operator = $operator; + $this->rightExpr = $rightExpr; + } + + /** + * @return mixed + */ + public function getLeftExpr() + { + return $this->leftExpr; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return mixed + */ + public function getRightExpr() + { + return $this->rightExpr; + } + + /** + * @return string + */ + public function __toString() + { + return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr; + } +} 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 00000000000..4d9a251986c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Composite extends Base +{ + /** + * @return string + */ + 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); + } + + /** + * @param string $part + * + * @return string + */ + 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; + } +} 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 00000000000..9dcce9bbe27 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL from. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class From +{ + /** + * @var string + */ + protected $from; + + /** + * @var string + */ + protected $alias; + + /** + * @var string + */ + protected $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 getIndexBy() + { + return $this->indexBy; + } + + /** + * @return string + */ + public function __toString() + { + return $this->from . ' ' . $this->alias . + ($this->indexBy ? ' INDEX BY ' . $this->indexBy : ''); + } +} 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 00000000000..b4ed07cd3b2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for generating DQL functions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Func +{ + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $arguments; + + /** + * Creates a function, with the given argument. + * + * @param string $name + * @param array $arguments + */ + public function __construct($name, $arguments) + { + $this->name = $name; + $this->arguments = (array) $arguments; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * @return string + */ + public function __toString() + { + return $this->name . '(' . implode(', ', $this->arguments) . ')'; + } +} 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 00000000000..efa3582bdb2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Group By parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupBy extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} 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 00000000000..7a59e247ae0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL join. + * + * @link www.doctrine-project.org + * @since 2.0 + * @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'; + + /** + * @var string + */ + protected $joinType; + + /** + * @var string + */ + protected $join; + + /** + * @var string + */ + protected $alias; + + /** + * @var string + */ + protected $conditionType; + + /** + * @var string + */ + protected $condition; + + /** + * @var string + */ + protected $indexBy; + + /** + * @param string $joinType The condition type constant. Either INNER_JOIN or LEFT_JOIN. + * @param string $join The relationship to join. + * @param string|null $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + */ + 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; + } + + /** + * @return string + */ + public function getJoinType() + { + return $this->joinType; + } + + /** + * @return string + */ + public function getJoin() + { + return $this->join; + } + + /** + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * @return string + */ + public function getConditionType() + { + return $this->conditionType; + } + + /** + * @return string + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return string + */ + public function getIndexBy() + { + return $this->indexBy; + } + + /** + * @return string + */ + public function __toString() + { + return strtoupper($this->joinType) . ' JOIN ' . $this->join + . ($this->alias ? ' ' . $this->alias : '') + . ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '') + . ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : ''); + } +} 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 00000000000..98cee79d7ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for generating DQL functions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Literal extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php new file mode 100644 index 00000000000..9bf800de8fe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL math statements. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Math +{ + /** + * @var mixed + */ + protected $leftExpr; + + /** + * @var string + */ + protected $operator; + + /** + * @var mixed + */ + protected $rightExpr; + + /** + * Creates a mathematical expression with the given arguments. + * + * @param mixed $leftExpr + * @param string $operator + * @param mixed $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpr = $leftExpr; + $this->operator = $operator; + $this->rightExpr = $rightExpr; + } + + /** + * @return mixed + */ + public function getLeftExpr() + { + return $this->leftExpr; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return mixed + */ + public function getRightExpr() + { + return $this->rightExpr; + } + + /** + * @return string + */ + 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; + } +} 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 00000000000..932548bd603 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Order By parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderBy +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $separator = ', '; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @var array + */ + protected $allowedClasses = array(); + + /** + * @var array + */ + protected $parts = array(); + + /** + * @param string|null $sort + * @param string|null $order + */ + public function __construct($sort = null, $order = null) + { + if ($sort) { + $this->add($sort, $order); + } + } + + /** + * @param string $sort + * @param string|null $order + * + * @return void + */ + public function add($sort, $order = null) + { + $order = ! $order ? 'ASC' : $order; + $this->parts[] = $sort . ' '. $order; + } + + /** + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } + + /** + * @return string + */ + public function __tostring() + { + return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator; + } +} 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 00000000000..c4ff7ae30d0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL OR clauses. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Orx extends Composite +{ + /** + * @var string + */ + protected $separator = ' OR '; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Andx', + 'Doctrine\ORM\Query\Expr\Orx', + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} 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 00000000000..21df6c735d4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL select statements. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Select extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Func' + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} 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 00000000000..b0248fbac15 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php @@ -0,0 +1,155 @@ +. + */ + +namespace Doctrine\ORM\Query\Filter; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use 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 EntityManagerInterface + */ + private $em; + + /** + * Parameters for the filter. + * + * @var array + */ + private $parameters = []; + + /** + * Constructs the SQLFilter object. + * + * @param EntityManagerInterface $em The entity manager. + */ + final public function __construct(EntityManagerInterface $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|null $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. + * + * @throws \InvalidArgumentException + */ + 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']); + } + + /** + * Checks if a parameter was set for the filter. + * + * @param string $name Name of the parameter. + * + * @return boolean + */ + final public function hasParameter($name) + { + if (!isset($this->parameters[$name])) { + return false; + } + + return true; + } + + /** + * 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); + } + + /** + * Returns the database connection used by the entity manager + * + * @return \Doctrine\DBAL\Connection + */ + final protected function getConnection() + { + return $this->em->getConnection(); + } + + /** + * Gets the SQL query part to add to a query. + * + * @param ClassMetaData $targetEntity + * @param string $targetTableAlias + * + * @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 00000000000..ffbaeaf1293 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php @@ -0,0 +1,225 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * 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 \Doctrine\ORM\Query\Filter\SQLFilter[] + */ + private $enabledFilters = array(); + + /** + * @var string The filter hash from the last time the query was parsed. + */ + private $filterHash; + + /** + * @var integer The current state of this filter. + */ + private $filtersState = self::FILTERS_STATE_CLEAN; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->config = $em->getConfiguration(); + } + + /** + * Gets all the enabled filters. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter[] The enabled filters. + */ + public function getEnabledFilters() + { + return $this->enabledFilters; + } + + /** + * Enables a filter from the collection. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The enabled filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + */ + public function enable($name) + { + if ( ! $this->has($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' does not exist."); + } + + if ( ! $this->isEnabled($name)) { + $filterClass = $this->config->getFilterClassName($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 \Doctrine\ORM\Query\Filter\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; + } + + /** + * Gets an enabled filter from the collection. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The filter. + * + * @throws \InvalidArgumentException If the filter is not enabled. + */ + public function getFilter($name) + { + if ( ! $this->isEnabled($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled."); + } + + return $this->enabledFilters[$name]; + } + + /** + * Checks whether filter with given name is defined. + * + * @param string $name Name of the filter. + * + * @return bool true if the filter exists, false if not. + */ + public function has($name) + { + return null !== $this->config->getFilterClassName($name); + } + + /** + * Checks if a filter is enabled. + * + * @param string $name Name of the filter. + * + * @return boolean True if the filter is enabled, false otherwise. + */ + public function isEnabled($name) + { + return isset($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; + } + + /** + * Sets 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 00000000000..d5721a7357a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php @@ -0,0 +1,207 @@ +. + */ + +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_SOME = 147; + const T_SUM = 148; + const T_THEN = 149; + const T_TRAILING = 150; + const T_TRUE = 151; + const T_UPDATE = 152; + const T_WHEN = 153; + const T_WHERE = 154; + const T_WITH = 155; + const T_PARTIAL = 156; + const T_NEW = 157; + + /** + * 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_][a-z0-9_]*' + ); + } + + /** + * @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; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php new file mode 100644 index 00000000000..39e2a7a4f21 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Defines a Query Parameter. + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class Parameter +{ + /** + * The parameter name. + * + * @var string + */ + private $name; + + /** + * The parameter value. + * + * @var mixed + */ + private $value; + + /** + * The parameter type. + * + * @var mixed + */ + private $type; + + /** + * Constructor. + * + * @param string $name Parameter name + * @param mixed $value Parameter value + * @param mixed $type Parameter type + */ + public function __construct($name, $value, $type = null) + { + $this->name = trim($name, ':'); + + $this->setValue($value, $type); + } + + /** + * Retrieves the Parameter name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Retrieves the Parameter value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Retrieves the Parameter type. + * + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * Defines the Parameter value. + * + * @param mixed $value Parameter value. + * @param mixed $type Parameter type. + */ + public function setValue($value, $type = null) + { + $this->value = $value; + $this->type = $type ?: ParameterTypeInferer::inferType($value); + } +} 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 00000000000..a12a559a730 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Provides an enclosed support for parameter inferring. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ParameterTypeInferer +{ + /** + * Infers 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) + { + if (is_integer($value)) { + return Type::INTEGER; + } + + if (is_bool($value)) { + return Type::BOOLEAN; + } + + if ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { + return Type::DATETIME; + } + + if (is_array($value)) { + return is_integer(current($value)) + ? Connection::PARAM_INT_ARRAY + : Connection::PARAM_STR_ARRAY; + } + + return \PDO::PARAM_STR; + } +} 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 00000000000..695e7ed1089 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php @@ -0,0 +1,3504 @@ +. + */ + +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 + * @author Fabio B. Silva + */ +class Parser +{ + /** + * READ-ONLY: Maps BUILT-IN string function names to AST class names. + * + * @var array + */ + 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. + * + * @var array + */ + 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. + * + * @var array + */ + 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. + */ + + /** + * @var array + */ + private $deferredIdentificationVariables = array(); + + /** + * @var array + */ + private $deferredPartialObjectExpressions = array(); + + /** + * @var array + */ + private $deferredPathExpressions = array(); + + /** + * @var array + */ + private $deferredResultVariables = array(); + + /** + * @var array + */ + private $deferredNewObjectExpressions = array(); + + /** + * The lexer. + * + * @var \Doctrine\ORM\Query\Lexer + */ + private $lexer; + + /** + * The parser result. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $parserResult; + + /** + * The EntityManager. + * + * @var \Doctrine\ORM\EntityManager + */ + 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(); + + /** + * Checks if a function is internally defined. Used to prevent overwriting + * of built-in functions through user-defined functions. + * + * @param string $functionName + * + * @return bool + */ + static public function isInternalFunction($functionName) + { + $functionName = strtolower($functionName); + + return isset(self::$_STRING_FUNCTIONS[$functionName]) + || isset(self::$_DATETIME_FUNCTIONS[$functionName]) + || isset(self::$_NUMERIC_FUNCTIONS[$functionName]); + } + + /** + * 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 + * + * @return void + */ + public function setCustomOutputTreeWalker($className) + { + $this->customOutputWalker = $className; + } + + /** + * Adds a custom tree walker for modifying the AST. + * + * @param string $className + * + * @return void + */ + 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 \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Parses and builds 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(); + } + + if ($this->deferredNewObjectExpressions) { + $this->processDeferredNewObjectExpressions($AST); + } + + $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 The token type. + * + * @return void + * + * @throws QueryException If the tokens don't 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(); + } + + /** + * Frees this parser, enabling it to be reused. + * + * @param boolean $deep Whether to clean peek and reset errors. + * @param integer $position Position to reset. + * + * @return void + */ + 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); + } + + $this->queryComponents = $treeWalkerChain->getQueryComponents(); + } + + $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; + } + + /** + * Fixes 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|null $token Got token. + * + * @return void + * + * @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, QueryException::dqlError($this->query->getDQL())); + } + + /** + * Generates a new semantical error. + * + * @param string $message Optional message. + * @param array|null $token Optional token. + * + * @return void + * + * @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, QueryException::dqlError($this->query->getDQL())); + } + + /** + * Peeks beyond the matched closing parenthesis and returns the first token after that one. + * + * @param boolean $resetPeek Reset peek after finding the closing parenthesis. + * + * @return array + */ + private function peekBeyondClosingParenthesis($resetPeek = true) + { + $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(); + } + + if ($resetPeek) { + $this->lexer->resetPeek(); + } + + return $token; + } + + /** + * Checks if the given token indicates a mathematical operator. + * + * @param array $token + * + * @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() + { + $lookaheadType = $this->lexer->lookahead['type']; + $peek = $this->lexer->peek(); + + $this->lexer->resetPeek(); + + return ($lookaheadType >= Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_OPEN_PARENTHESIS); + } + + /** + * Checks whether the given token type indicates an aggregate function. + * + * @param int $tokenType + * + * @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 NewObjectExpression. + * + * @param \Doctrine\ORM\Query\AST\SelectClause $AST + * + * @return void + */ + private function processDeferredNewObjectExpressions($AST) + { + foreach ($this->deferredNewObjectExpressions as $deferredItem) { + $expression = $deferredItem['expression']; + $token = $deferredItem['token']; + $className = $expression->className; + $args = $expression->args; + $fromClassName = isset($AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName) + ? $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName + : null; + + // If the namespace is not given then assumes the first FROM entity namespace + if (strpos($className, '\\') === false && ! class_exists($className) && strpos($fromClassName, '\\') !== false) { + $namespace = substr($fromClassName, 0 , strrpos($fromClassName, '\\')); + $fqcn = $namespace . '\\' . $className; + + if (class_exists($fqcn)) { + $expression->className = $fqcn; + $className = $fqcn; + } + } + + if ( ! class_exists($className)) { + $this->semanticalError(sprintf('Class "%s" is not defined.', $className), $token); + } + + $class = new \ReflectionClass($className); + + if ( ! $class->isInstantiable()) { + $this->semanticalError(sprintf('Class "%s" can not be instantiated.', $className), $token); + } + + if ($class->getConstructor() === null) { + $this->semanticalError(sprintf('Class "%s" has not a valid constructor.', $className), $token); + } + + if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) { + $this->semanticalError(sprintf('Number of arguments does not match with "%s" constructor declaration.', $className), $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; + } + + if (isset($class->associationMappings[$field]) && + $class->associationMappings[$field]['isOwningSide'] && + $class->associationMappings[$field]['type'] & ClassMetadata::TO_ONE) { + 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( + "'$resultVariable' 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 mixed $AST + * + * @return void + */ + 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; + } + } + + /** + * @return void + */ + 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) || interface_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() + { + $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 ( ! $class->hasAssociation($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) + { + $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']; + + while ($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 deferred + $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 ::= ( + * SimpleArithmeticExpression | SingleValuedPathExpression | + * ScalarExpression | ResultVariable | FunctionDeclaration + * ) ["ASC" | "DESC"] + * + * @return \Doctrine\ORM\Query\AST\OrderByItem + */ + public function OrderByItem() + { + $this->lexer->peek(); // lookahead => '.' + $this->lexer->peek(); // lookahead => token after '.' + + $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' + + $this->lexer->resetPeek(); + + $glimpse = $this->lexer->glimpse(); + + switch (true) { + case ($this->isFunction($peek)): + $expr = $this->FunctionDeclaration(); + break; + + case ($this->isMathOperator($peek)): + $expr = $this->SimpleArithmeticExpression(); + break; + + case ($glimpse['type'] === Lexer::T_DOT): + $expr = $this->SingleValuedPathExpression(); + break; + + case ($this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis())): + $expr = $this->ScalarExpression(); + break; + + default: + $expr = $this->ResultVariable(); + break; + } + + $type = 'ASC'; + $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 SimpleEntityExpression + * + * @return AST\ArithmeticExpression + */ + 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->ArithmeticExpression(); + } + + /** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* + * + * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function IdentificationVariableDeclaration() + { + $joins = array(); + $rangeVariableDeclaration = $this->RangeVariableDeclaration(); + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) + ? $this->IndexBy() + : null; + + $rangeVariableDeclaration->isRoot = true; + + while ( + $this->lexer->isNextToken(Lexer::T_LEFT) || + $this->lexer->isNextToken(Lexer::T_INNER) || + $this->lexer->isNextToken(Lexer::T_JOIN) + ) { + $joins[] = $this->Join(); + } + + return new AST\IdentificationVariableDeclaration( + $rangeVariableDeclaration, $indexBy, $joins + ); + } + + /** + * + * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration + * + * {Internal note: WARNING: Solution is harder than a bare implementation. + * Desired EBNF support: + * + * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable) + * + * It demands that entire SQL generation to become programmatical. This is + * needed because association based subselect requires "WHERE" conditional + * expressions to be injected, but there is no scope to do that. Only scope + * accessible is "FROM", prohibiting an easy implementation without larger + * changes.} + * + * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration | + * \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function SubselectIdentificationVariableDeclaration() + { + /* + NOT YET IMPLEMENTED! + + $glimpse = $this->lexer->glimpse(); + + if ($glimpse['type'] == Lexer::T_DOT) { + $associationPathExpression = $this->AssociationPathExpression(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $identificationVariable = $associationPathExpression->identificationVariable; + $field = $associationPathExpression->associationField; + + $class = $this->queryComponents[$identificationVariable]['metadata']; + $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']); + + // Building queryComponent + $joinQueryComponent = array( + 'metadata' => $targetClass, + 'parent' => $identificationVariable, + 'relation' => $class->getAssociationMapping($field), + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->lookahead + ); + + $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; + + return new AST\SubselectIdentificationVariableDeclaration( + $associationPathExpression, $aliasIdentificationVariable + ); + } + */ + + return $this->IdentificationVariableDeclaration(); + } + + /** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" + * (JoinAssociationDeclaration | RangeVariableDeclaration) + * ["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); + + $next = $this->lexer->glimpse(); + $joinDeclaration = ($next['type'] === Lexer::T_DOT) ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration(); + $adhocConditions = $this->lexer->isNextToken(Lexer::T_WITH); + $join = new AST\Join($joinType, $joinDeclaration); + + // Describe non-root join declaration + if ($joinDeclaration instanceof AST\RangeVariableDeclaration) { + $joinDeclaration->isRoot = false; + + $adhocConditions = true; + } + + // Check for ad-hoc Join conditions + if ($adhocConditions) { + $this->match(Lexer::T_WITH); + + $join->conditionalExpression = $this->ConditionalExpression(); + } + + return $join; + } + + /** + * 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); + } + + /** + * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy] + * + * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression + */ + public function JoinAssociationDeclaration() + { + $joinAssociationPathExpression = $this->JoinAssociationPathExpression(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + + $identificationVariable = $joinAssociationPathExpression->identificationVariable; + $field = $joinAssociationPathExpression->associationField; + + $class = $this->queryComponents[$identificationVariable]['metadata']; + $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']); + + // Building queryComponent + $joinQueryComponent = array( + 'metadata' => $targetClass, + 'parent' => $joinAssociationPathExpression->identificationVariable, + 'relation' => $class->getAssociationMapping($field), + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->lookahead + ); + + $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; + + return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy); + } + + /** + * 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); + + $field = $this->lexer->token['value']; + + while ($this->lexer->isNextToken(Lexer::T_DOT)) { + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + $field .= '.'.$this->lexer->token['value']; + } + + $partialFieldSet[] = $field; + } + + $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; + } + + /** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + * + * @return \Doctrine\ORM\Query\AST\NewObjectExpression + */ + public function NewObjectExpression() + { + $this->match(Lexer::T_NEW); + $this->match(Lexer::T_IDENTIFIER); + + $token = $this->lexer->token; + $className = $token['value']; + + if (strrpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + + $className = $this->em->getConfiguration() + ->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $args[] = $this->NewObjectArg(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $args[] = $this->NewObjectArg(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + $expression = new AST\NewObjectExpression($className, $args); + + // Defer NewObjectExpression validation + $this->deferredNewObjectExpressions[] = array( + 'token' => $token, + 'expression' => $expression, + 'nestingLevel' => $this->nestingLevel, + ); + + return $expression; + } + + /** + * NewObjectArg ::= ScalarExpression | "(" Subselect ")" + * + * @return mixed + */ + public function NewObjectArg() + { + $token = $this->lexer->lookahead; + $peek = $this->lexer->glimpse(); + + if ($token['type'] === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expression; + } + + return $this->ScalarExpression(); + } + + /** + * 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']; + $peek = $this->lexer->glimpse(); + + switch (true) { + case ($lookahead === Lexer::T_INTEGER): + case ($lookahead === Lexer::T_FLOAT): + // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) or ( - 1 ) or ( + 1 ) + case ($lookahead === Lexer::T_MINUS): + case ($lookahead === Lexer::T_PLUS): + return $this->SimpleArithmeticExpression(); + + case ($lookahead === Lexer::T_STRING): + return $this->StringPrimary(); + + case ($lookahead === Lexer::T_TRUE): + case ($lookahead === Lexer::T_FALSE): + $this->match($lookahead); + + return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); + + case ($lookahead === Lexer::T_INPUT_PARAMETER): + switch (true) { + case $this->isMathOperator($peek): + // :param + u.value + return $this->SimpleArithmeticExpression(); + + default: + return $this->InputParameter(); + } + + case ($lookahead === Lexer::T_CASE): + case ($lookahead === Lexer::T_COALESCE): + case ($lookahead === Lexer::T_NULLIF): + // Since NULLIF and COALESCE can be identified as a function, + // we need to check these before checking for FunctionDeclaration + return $this->CaseExpression(); + + case ($lookahead === Lexer::T_OPEN_PARENTHESIS): + return $this->SimpleArithmeticExpression(); + + // this check must be done before checking for a filed path expression + case ($this->isFunction()): + $this->lexer->peek(); // "(" + + switch (true) { + case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): + // SUM(u.id) + COUNT(u.id) + return $this->SimpleArithmeticExpression(); + + case ($this->isAggregateFunction($this->lexer->lookahead['type'])): + return $this->AggregateExpression(); + + default: + // IDENTITY(u) + return $this->FunctionDeclaration(); + } + + break; + // it is no function, so it must be a field path + case ($lookahead === 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(); + + default: + $this->syntaxError(); + } + } + + /** + * 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\GeneralCaseExpression + */ + 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 + * + * @return AST\SimpleCaseExpression + */ + 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\WhenClause + */ + 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\SimpleWhenClause + */ + 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 | NewObjectExpression + * ) [["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; + + // NewObjectExpression (New ClassName(id, name)) + case ($lookaheadType === Lexer::T_NEW): + $expression = $this->NewObjectExpression(); + 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 | FunctionDeclaration | + * 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); + + case ($this->isFunction()): + // SUM(u.id) + COUNT(u.id) + if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) { + return new AST\SimpleSelectExpression($this->ScalarExpression()); + } + // COUNT(u.id) + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { + return new AST\SimpleSelectExpression($this->AggregateExpression()); + } + // IDENTITY(u) + return new AST\SimpleSelectExpression($this->FunctionDeclaration()); + + 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 parenthesis ')' + $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() + { + if ($this->lexer->isNextToken(Lexer::T_EXISTS)) { + return $this->ExistsExpression(); + } + + $token = $this->lexer->lookahead; + $peek = $this->lexer->glimpse(); + $lookahead = $token; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $token = $this->lexer->glimpse(); + } + + if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER || $this->isFunction()) { + // Peek beyond the matching closing parenthesis. + $beyond = $this->lexer->peek(); + + switch ($peek['value']) { + case '(': + // Peeks beyond the matched closing parenthesis. + $token = $this->peekBeyondClosingParenthesis(false); + + if ($token['type'] === Lexer::T_NOT) { + $token = $this->lexer->peek(); + } + + if ($token['type'] === Lexer::T_IS) { + $lookahead = $this->lexer->peek(); + } + break; + + default: + // Peek beyond the PathExpression or InputParameter. + $token = $beyond; + + while ($token['value'] === '.') { + $this->lexer->peek(); + + $token = $this->lexer->peek(); + } + + // Also peek beyond a NOT if there is one. + if ($token['type'] === Lexer::T_NOT) { + $token = $this->lexer->peek(); + } + + // We need to go even further in case of IS (differentiate 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(); + } + + if ($token['type'] === Lexer::T_BETWEEN) { + return $this->BetweenExpression(); + } + + if ($token['type'] === Lexer::T_LIKE) { + return $this->LikeExpression(); + } + + if ($token['type'] === Lexer::T_IN) { + return $this->InExpression(); + } + + if ($token['type'] === Lexer::T_INSTANCE) { + return $this->InstanceOfExpression(); + } + + if ($token['type'] === Lexer::T_MEMBER) { + return $this->CollectionMemberExpression(); + } + + if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_NULL) { + return $this->NullComparisonExpression(); + } + + if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_EMPTY) { + return $this->EmptyCollectionComparisonExpression(); + } + + return $this->ComparisonExpression(); + } + + /** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression + */ + public function EmptyCollectionComparisonExpression() + { + $emptyCollectionCompExpr = new AST\EmptyCollectionComparisonExpression( + $this->CollectionValuedPathExpression() + ); + $this->match(Lexer::T_IS); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $emptyCollectionCompExpr->not = true; + } + + $this->match(Lexer::T_EMPTY); + + return $emptyCollectionCompExpr; + } + + /** + * 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 \Doctrine\ORM\Query\AST\Literal + */ + 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 | ParenthesisExpression + * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings + * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable + * | InputParameter | 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 new AST\ParenthesisExpression($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 | ResultVariable | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\StringPrimary | + * \Doctrine\ORM\Query\AST\Subselect | + * string + */ + public function StringExpression() + { + $peek = $this->lexer->glimpse(); + + // Subselect + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && $peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + + // ResultVariable (string) + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && + isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) { + return $this->ResultVariable(); + } + + 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 doesn't check for custom functions. + return $this->FunctionDeclaration(); + } + + $this->syntaxError("'.' or '('"); + break; + + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + + return new AST\Literal(AST\Literal::STRING, $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" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")" + * + * @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 = $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() + { + $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" StringPrimary ["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 { + $stringPattern = $this->StringPrimary(); + } + + $escapeChar = null; + + if ($this->lexer->lookahead['type'] === Lexer::T_ESCAPE) { + $this->match(Lexer::T_ESCAPE); + $this->match(Lexer::T_STRING); + + $escapeChar = new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); + } + + $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar); + $likeExpr->not = $not; + + return $likeExpr; + } + + /** + * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" + * + * @return \Doctrine\ORM\Query\AST\NullComparisonExpression + */ + public function NullComparisonExpression() + { + switch (true) { + case $this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER): + $this->match(Lexer::T_INPUT_PARAMETER); + + $expr = new AST\InputParameter($this->lexer->token['value']); + break; + + case $this->lexer->isNextToken(Lexer::T_NULLIF): + $expr = $this->NullIfExpression(); + break; + + case $this->lexer->isNextToken(Lexer::T_COALESCE): + $expr = $this->CoalesceExpression(); + break; + + case $this->isAggregateFunction($this->lexer->lookahead['type']): + $expr = $this->AggregateExpression(); + break; + + case $this->isFunction(): + $expr = $this->FunctionDeclaration(); + break; + + default: + // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression + $glimpse = $this->lexer->glimpse(); + + if ($glimpse['type'] === Lexer::T_DOT) { + $expr = $this->SingleValuedPathExpression(); + + // Leave switch statement + break; + } + + $lookaheadValue = $this->lexer->lookahead['value']; + + // Validate existing component + if ( ! isset($this->queryComponents[$lookaheadValue])) { + $this->semanticalError('Cannot add having condition on undefined result variable.'); + } + + // Validating ResultVariable + if ( ! isset($this->queryComponents[$lookaheadValue]['resultVariable'])) { + $this->semanticalError('Cannot add having condition on a non result variable.'); + } + + $expr = $this->ResultVariable(); + break; + } + + $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 + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + 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. + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + 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 ")" | + * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningNumerics() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningNumerics() + { + // getCustomNumericFunction is case-insensitive + $functionName = strtolower($this->lexer->lookahead['value']); + $functionClass = $this->em->getConfiguration()->getCustomNumericFunction($functionName); + + $function = is_string($functionClass) + ? new $functionClass($functionName) + : call_user_func($functionClass, $functionName); + + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningDateTime ::= + * "CURRENT_DATE" | + * "CURRENT_TIME" | + * "CURRENT_TIMESTAMP" | + * "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" | + * "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningDatetime() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningDatetime() + { + // getCustomDatetimeFunction is case-insensitive + $functionName = $this->lexer->lookahead['value']; + $functionClass = $this->em->getConfiguration()->getCustomDatetimeFunction($functionName); + + $function = is_string($functionClass) + ? new $functionClass($functionName) + : call_user_func($functionClass, $functionName); + + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningStrings ::= + * "CONCAT" "(" StringPrimary "," StringPrimary ")" | + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + * "LOWER" "(" StringPrimary ")" | + * "UPPER" "(" StringPrimary ")" | + * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningStrings() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_STRING_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningStrings() + { + // getCustomStringFunction is case-insensitive + $functionName = $this->lexer->lookahead['value']; + $functionClass = $this->em->getConfiguration()->getCustomStringFunction($functionName); + + $function = is_string($functionClass) + ? new $functionClass($functionName) + : call_user_func($functionClass, $functionName); + + $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 00000000000..dfc7361f8ba --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php @@ -0,0 +1,145 @@ +. + */ + +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/mit-license.php MIT + * @link http://www.doctrine-project.org + * @since 2.0 + */ +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|null 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 + * + * @return void + */ + 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 + * + * @return void + */ + 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 + * + * @return void + */ + 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]; + } +} 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 00000000000..d92ad850f5b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A parse tree printer for Doctrine Query Language parser. + * + * @author Janne Vanhala + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://www.phpdoctrine.org + * @since 2.0 + */ +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. + * + * @return void + */ + 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. + * + * @return void + */ + public function endProduction() + { + $this->_indent--; + $this->println(')'); + } + + /** + * Prints text indented with spaces depending on current indentation level. + * + * @param string $str The text. + * + * @return void + */ + public function println($str) + { + if ( ! $this->_silent) { + echo str_repeat(' ', $this->_indent), $str, "\n"; + } + } +} 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 00000000000..16a10856acc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php @@ -0,0 +1,266 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Description of QueryException. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class QueryException extends \Doctrine\ORM\ORMException +{ + /** + * @param string $dql + * + * @return QueryException + */ + public static function dqlError($dql) + { + return new self($dql); + } + + /** + * @param string $message + * @param \Exception|null $previous + * + * @return QueryException + */ + public static function syntaxError($message, $previous = null) + { + return new self('[Syntax Error] ' . $message, 0, $previous); + } + + /** + * @param string $message + * @param \Exception|null $previous + * + * @return QueryException + */ + public static function semanticalError($message, $previous = null) + { + return new self('[Semantical Error] ' . $message, 0, $previous); + } + + /** + * @return QueryException + */ + public static function invalidLockMode() + { + return new self('Invalid lock mode hint provided.'); + } + + /** + * @param string $expected + * @param string $received + * + * @return QueryException + */ + public static function invalidParameterType($expected, $received) + { + return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.'); + } + + /** + * @param string $pos + * + * @return QueryException + */ + public static function invalidParameterPosition($pos) + { + return new self('Invalid parameter position: ' . $pos); + } + + /** + * @param integer $expected + * @param integer $received + * + * @return QueryException + */ + public static function tooManyParameters($expected, $received) + { + return new self('Too many parameters: the query defines ' . $expected . ' parameters and you bound ' . $received); + } + + /** + * @param integer $expected + * @param integer $received + * + * @return QueryException + */ + public static function tooFewParameters($expected, $received) + { + return new self('Too few parameters: the query defines ' . $expected . ' parameters but you only bound ' . $received); + } + + /** + * @param string $value + * + * @return QueryException + */ + public static function invalidParameterFormat($value) + { + return new self('Invalid parameter format, '.$value.' given, but : or ? expected.'); + } + + /** + * @param string $key + * + * @return QueryException + */ + public static function unknownParameter($key) + { + return new self("Invalid parameter: token ".$key." is not defined in the query."); + } + + /** + * @return QueryException + */ + public static function parameterTypeMismatch() + { + return new self("DQL Query parameter and type numbers mismatch, but have to be exactly equal."); + } + + /** + * @param object $pathExpr + * + * @return QueryException + */ + public static function invalidPathExpression($pathExpr) + { + return new self( + "Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'." + ); + } + + /** + * @param string $literal + * + * @return QueryException + */ + public static function invalidLiteral($literal) + { + return new self("Invalid literal '$literal'"); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function iterateWithFetchJoinCollectionNotAllowed($assoc) + { + return new self( + "Invalid query operation: Not allowed to iterate over fetch join collections ". + "in class ".$assoc['sourceEntity']." association ".$assoc['fieldName'] + ); + } + + /** + * @return QueryException + */ + 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." + ); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + 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']." association ".$assoc['fieldName'].". ". + "Use WITH to append additional join conditions to the association." + ); + } + + /** + * @return QueryException + */ + 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." + ); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function iterateWithFetchJoinNotAllowed($assoc) + { + return new self( + "Iterate with fetch join in class " . $assoc['sourceEntity'] . + " using association " . $assoc['fieldName'] . " not allowed." + ); + } + + /** + * @return QueryException + */ + 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." + ); + } + + /** + * @param string $className + * @param string $rootClass + * + * @return QueryException + */ + public static function instanceOfUnrelatedClass($className, $rootClass) + { + return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " . + "inheritance hierarchy does not exists between these two classes."); + } + + /** + * @param string $dqlAlias + * + * @return QueryException + */ + public static function invalidQueryComponent($dqlAlias) + { + return new self( + "Invalid query component given for DQL alias '" . $dqlAlias . "', ". + "requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys." + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php new file mode 100644 index 00000000000..fe547081f38 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php @@ -0,0 +1,211 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\Common\Collections\Expr\Value; + +/** + * Converts Collection expressions to Query expressions. + * + * @author Kirill chEbba Chebunin + * @since 2.4 + */ +class QueryExpressionVisitor extends ExpressionVisitor +{ + /** + * @var array + */ + private static $operatorMap = array( + Comparison::GT => Expr\Comparison::GT, + Comparison::GTE => Expr\Comparison::GTE, + Comparison::LT => Expr\Comparison::LT, + Comparison::LTE => Expr\Comparison::LTE + ); + + /** + * @var array + */ + private $queryAliases; + + /** + * @var Expr + */ + private $expr; + + /** + * @var array + */ + private $parameters = array(); + + /** + * Constructor + * + * @param array $queryAliases + */ + public function __construct($queryAliases) + { + $this->queryAliases = $queryAliases; + $this->expr = new Expr(); + } + + /** + * Gets bound parameters. + * Filled after {@link dispach()}. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function getParameters() + { + return new ArrayCollection($this->parameters); + } + + /** + * Clears parameters. + * + * @return void + */ + public function clearParameters() + { + $this->parameters = array(); + } + + /** + * Converts Criteria expression to Query one based on static map. + * + * @param string $criteriaOperator + * + * @return string|null + */ + private static function convertComparisonOperator($criteriaOperator) + { + return isset(self::$operatorMap[$criteriaOperator]) ? self::$operatorMap[$criteriaOperator] : null; + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return new Expr\Andx($expressionList); + + case CompositeExpression::TYPE_OR: + return new Expr\Orx($expressionList); + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + + if ( ! isset($this->queryAliases[0])) { + throw new QueryException('No aliases are set before invoking walkComparison().'); + } + + $field = $this->queryAliases[0] . '.' . $comparison->getField(); + + foreach($this->queryAliases as $alias) { + if(strpos($comparison->getField() . '.', $alias . '.') === 0) { + $field = $comparison->getField(); + break; + } + } + + $parameterName = str_replace('.', '_', $comparison->getField()); + + foreach($this->parameters as $parameter) { + if($parameter->getName() === $parameterName) { + $parameterName .= '_' . count($this->parameters); + break; + } + } + + $parameter = new Parameter($parameterName, $this->walkValue($comparison->getValue())); + $placeholder = ':' . $parameterName; + + switch ($comparison->getOperator()) { + case Comparison::IN: + $this->parameters[] = $parameter; + return $this->expr->in($field, $placeholder); + + case Comparison::NIN: + $this->parameters[] = $parameter; + return $this->expr->notIn($field, $placeholder); + + case Comparison::EQ: + case Comparison::IS: + if ($this->walkValue($comparison->getValue()) === null) { + return $this->expr->isNull($field); + } + $this->parameters[] = $parameter; + return $this->expr->eq($field, $placeholder); + + case Comparison::NEQ: + if ($this->walkValue($comparison->getValue()) === null) { + return $this->expr->isNotNull($field); + } + $this->parameters[] = $parameter; + return $this->expr->neq($field, $placeholder); + + case Comparison::CONTAINS: + $parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType()); + $this->parameters[] = $parameter; + return $this->expr->like($field, $placeholder); + + default: + $operator = self::convertComparisonOperator($comparison->getOperator()); + if ($operator) { + $this->parameters[] = $parameter; + return new Expr\Comparison( + $field, + $operator, + $placeholder + ); + } + + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } +} 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 00000000000..bdd5de75eff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -0,0 +1,578 @@ +. + */ + +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 +{ + /** + * Whether the result is mixed (contains scalar values together with field values). + * + * @ignore + * @var boolean + */ + public $isMixed = false; + + /** + * Whether the result is a select statement. + * + * @ignore + * @var boolean + */ + public $isSelect = true; + + /** + * Maps alias names to class names. + * + * @ignore + * @var array + */ + public $aliasMap = array(); + + /** + * Maps alias names to related association field names. + * + * @ignore + * @var array + */ + public $relationMap = array(); + + /** + * Maps alias names to parent alias names. + * + * @ignore + * @var array + */ + public $parentAliasMap = array(); + + /** + * Maps column names in the result set to field names for each class. + * + * @ignore + * @var array + */ + public $fieldMappings = array(); + + /** + * Maps column names in the result set to the alias/field name to use in the mapped result. + * + * @ignore + * @var array + */ + public $scalarMappings = array(); + + /** + * Maps column names in the result set to the alias/field type to use in the mapped result. + * + * @ignore + * @var array + */ + public $typeMappings = array(); + + /** + * Maps entities in the result set to the alias name to use in the mapped result. + * + * @ignore + * @var array + */ + public $entityMappings = array(); + + /** + * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names. + * + * @ignore + * @var array + */ + public $metaMappings = array(); + + /** + * Maps column names in the result set to the alias they belong to. + * + * @ignore + * @var array + */ + public $columnOwnerMap = array(); + + /** + * List of columns in the result set that are used as discriminator columns. + * + * @ignore + * @var array + */ + public $discriminatorColumns = array(); + + /** + * Maps alias names to field names that should be used for indexing. + * + * @ignore + * @var array + */ + public $indexByMap = array(); + + /** + * Map from column names to class names that declare the field the column is mapped to. + * + * @ignore + * @var array + */ + public $declaringClasses = array(); + + /** + * This is necessary to hydrate derivate foreign keys correctly. + * + * @var array + */ + public $isIdentifierColumn = array(); + + /** + * Maps column names in the result set to field names for each new object expression. + * + * @var array + */ + public $newObjectMappings = array(); + + /** + * Maps metadata parameter names to the metadata attribute. + * + * @var array + */ + public $metadataParameterMapping = 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|null $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 (array_merge($this->metaMappings, $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; + } + + /** + * Sets to index by a scalar result column name. + * + * @param string $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 string $alias + * @param string $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|null $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 string $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 = 'string') + { + $this->scalarMappings[$columnName] = $alias; + $this->typeMappings[$columnName] = $type; + + if ( ! $this->isMixed && $this->fieldMappings) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Adds a metadata parameter mappings. + * + * @param mixed $parameter The parameter name in the SQL result set. + * @param string $attribute The metadata attribute. + */ + public function addMetadataParameterMapping($parameter, $attribute) + { + $this->metadataParameterMapping[$parameter] = $attribute; + } + + /** + * Checks whether a column with a given name is mapped as a scalar result. + * + * @param string $columnName 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 The result alias with which the meta result should be placed in the result structure. + * @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 bool $isIdentifierColumn + * @param string $type The column type + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null) + { + $this->metaMappings[$columnName] = $fieldName; + $this->columnOwnerMap[$columnName] = $alias; + + if ($isIdentifierColumn) { + $this->isIdentifierColumn[$alias][$columnName] = true; + } + + if ($type) { + $this->typeMappings[$columnName] = $type; + } + + 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 00000000000..804a208efbe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -0,0 +1,458 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManagerInterface; +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 +{ + /** + * Picking this rename mode will register entity columns as is, + * as they are in the database. This can cause clashes when multiple + * entities are fetched that have columns with the same name. + * + * @var int + */ + const COLUMN_RENAMING_NONE = 1; + + /** + * Picking custom renaming allows the user to define the renaming + * of specific columns with a rename array that contains column names as + * keys and result alias as values. + * + * @var int + */ + const COLUMN_RENAMING_CUSTOM = 2; + + /** + * Incremental renaming uses a result set mapping internal counter to add a + * number to each column result, leading to uniqueness. This only works if + * you use {@see generateSelectClause()} to generate the SELECT clause for + * you. + * + * @var int + */ + const COLUMN_RENAMING_INCREMENT = 3; + + /** + * @var int + */ + private $sqlCounter = 0; + + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * Default column renaming mode. + * + * @var int + */ + private $defaultRenameMode; + + /** + * @param EntityManagerInterface $em + * @param integer $defaultRenameMode + */ + public function __construct(EntityManagerInterface $em, $defaultRenameMode = self::COLUMN_RENAMING_NONE) + { + $this->em = $em; + $this->defaultRenameMode = $defaultRenameMode; + } + + /** + * 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). + * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). + * + * @return void + */ + public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array(), $renameMode = null) + { + $renameMode = $renameMode ?: $this->defaultRenameMode; + $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); + + $this->addEntityResult($class, $alias); + $this->addAllClassFields($class, $alias, $columnAliasMap); + } + + /** + * 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). + * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). + * + * @return void + */ + public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array(), $renameMode = null) + { + $renameMode = $renameMode ?: $this->defaultRenameMode; + $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); + + $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation); + $this->addAllClassFields($class, $alias, $columnAliasMap); + } + + /** + * Adds all fields of the given class to the result set mapping (columns and meta fields). + * + * @param string $class + * @param string $alias + * @param array $columnAliasMap + * + * @return void + * + * @throws \InvalidArgumentException + */ + protected function addAllClassFields($class, $alias, $columnAliasMap = array()) + { + $classMetadata = $this->em->getClassMetadata($class); + $platform = $this->em->getConnection()->getDatabasePlatform(); + + if ( ! $this->isInheritanceSupported($classMetadata)) { + throw new \InvalidArgumentException('ResultSetMapping builder does not currently support your inheritance scheme.'); + } + + + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $columnAlias = $platform->getSQLResultCasing($columnAliasMap[$columnName]); + + if (isset($this->fieldMappings[$columnAlias])) { + throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); + } + + $this->addFieldResult($alias, $columnAlias, $propertyName); + } + + foreach ($classMetadata->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $columnAlias = $platform->getSQLResultCasing($columnAliasMap[$columnName]); + + if (isset($this->metaMappings[$columnAlias])) { + throw new \InvalidArgumentException("The column '$columnAlias' conflicts with another column in the mapper."); + } + + $this->addMetaResult( + $alias, + $columnAlias, + $columnName, + (isset($associationMapping['id']) && $associationMapping['id'] === true) + ); + } + } + } + } + + private function isInheritanceSupported(ClassMetadataInfo $classMetadata) + { + if ($classMetadata->isInheritanceTypeSingleTable() + && in_array($classMetadata->name, $classMetadata->discriminatorMap, true)) { + return true; + } + + return ! ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()); + } + + /** + * Gets column alias for a given column. + * + * @param string $columnName + * @param int $mode + * @param array $customRenameColumns + * + * @return string + */ + private function getColumnAlias($columnName, $mode, array $customRenameColumns) + { + switch ($mode) { + case self::COLUMN_RENAMING_INCREMENT: + return $columnName . $this->sqlCounter++; + + case self::COLUMN_RENAMING_CUSTOM: + return isset($customRenameColumns[$columnName]) + ? $customRenameColumns[$columnName] : $columnName; + + case self::COLUMN_RENAMING_NONE: + return $columnName; + + } + } + + /** + * Retrieves a class columns and join columns aliases that are used in the SELECT clause. + * + * This depends on the renaming mode selected by the user. + * + * @param string $className + * @param int $mode + * @param array $customRenameColumns + * + * @return array + */ + private function getColumnAliasMap($className, $mode, array $customRenameColumns) + { + if ($customRenameColumns) { // for BC with 2.2-2.3 API + $mode = self::COLUMN_RENAMING_CUSTOM; + } + + $columnAlias = array(); + $class = $this->em->getClassMetadata($className); + + foreach ($class->getColumnNames() as $columnName) { + $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns); + } + + foreach ($class->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns); + } + } + } + + return $columnAlias; + } + + /** + * Adds the mappings of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param array $queryMapping + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryMapping(ClassMetadataInfo $class, array $queryMapping) + { + if (isset($queryMapping['resultClass'])) { + return $this->addNamedNativeQueryResultClassMapping($class, $queryMapping['resultClass']); + } + + return $this->addNamedNativeQueryResultSetMapping($class, $queryMapping['resultSetMapping']); + } + + /** + * Adds the class mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultClassName + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultClassMapping(ClassMetadataInfo $class, $resultClassName) + { + $classMetadata = $this->em->getClassMetadata($resultClassName); + $shortName = $classMetadata->reflClass->getShortName(); + $alias = strtolower($shortName[0]).'0'; + + $this->addEntityResult($class->name, $alias); + + if ($classMetadata->discriminatorColumn) { + $discriminatorColumn = $classMetadata->discriminatorColumn; + $this->setDiscriminatorColumn($alias, $discriminatorColumn['name']); + $this->addMetaResult($alias, $discriminatorColumn['name'], $discriminatorColumn['fieldName']); + } + + foreach ($classMetadata->getColumnNames() as $key => $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $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']; + $this->addMetaResult($alias, $columnName, $columnName, $classMetadata->isIdentifier($columnName)); + } + } + } + + return $this; + } + + /** + * Adds the result set mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultSetMappingName + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName) + { + $counter = 0; + $resultMapping = $class->getSqlResultSetMapping($resultSetMappingName); + $rooShortName = $class->reflClass->getShortName(); + $rootAlias = strtolower($rooShortName[0]) . $counter; + + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityMapping) { + $classMetadata = $this->em->getClassMetadata($entityMapping['entityClass']); + + if ($class->reflClass->name == $classMetadata->reflClass->name) { + $this->addEntityResult($classMetadata->name, $rootAlias); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias); + } else { + $shortName = $classMetadata->reflClass->getShortName(); + $joinAlias = strtolower($shortName[0]) . ++ $counter; + $associations = $class->getAssociationsByTargetClass($classMetadata->name); + + foreach ($associations as $relation => $mapping) { + $this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias); + } + } + + } + } + + if (isset($resultMapping['columns'])) { + foreach ($resultMapping['columns'] as $entityMapping) { + $this->addScalarResult($entityMapping['name'], $entityMapping['name']); + } + } + + return $this; + } + + /** + * Adds the entity result mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $classMetadata + * @param array $entityMapping + * @param string $alias + * + * @return ResultSetMappingBuilder + * + * @throws \InvalidArgumentException + */ + public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classMetadata, array $entityMapping, $alias) + { + if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) { + $discriminatorColumn = $entityMapping['discriminatorColumn']; + $this->setDiscriminatorColumn($alias, $discriminatorColumn); + $this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn); + } + + if (isset($entityMapping['fields']) && !empty($entityMapping['fields'])) { + foreach ($entityMapping['fields'] as $field) { + $fieldName = $field['name']; + $relation = null; + + if(strpos($fieldName, '.')){ + list($relation, $fieldName) = explode('.', $fieldName); + } + + if (isset($classMetadata->associationMappings[$relation])) { + if($relation) { + $associationMapping = $classMetadata->associationMappings[$relation]; + $joinAlias = $alias.$relation; + $parentAlias = $alias; + + $this->addJoinedEntityResult($associationMapping['targetEntity'], $joinAlias, $parentAlias, $relation); + $this->addFieldResult($joinAlias, $field['column'], $fieldName); + }else { + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } else { + if(!isset($classMetadata->fieldMappings[$fieldName])) { + throw new \InvalidArgumentException("Entity '".$classMetadata->name."' has no field '".$fieldName."'. "); + } + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } + + } else { + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $this->addFieldResult($alias, $columnName, $propertyName); + } + } + + return $this; + } + + /** + * Generates the Select clause from this ResultSetMappingBuilder. + * + * Works only for all the entity results. The select parts for scalar + * expressions have to be written manually. + * + * @param array $tableAliases + * + * @return string + */ + public function generateSelectClause($tableAliases = array()) + { + $sql = ""; + + foreach ($this->columnOwnerMap as $columnName => $dqlAlias) { + $tableAlias = isset($tableAliases[$dqlAlias]) + ? $tableAliases[$dqlAlias] : $dqlAlias; + + if ($sql) { + $sql .= ", "; + } + + $sql .= $tableAlias . "."; + + if (isset($this->fieldMappings[$columnName])) { + $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]); + $sql .= $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName']; + } else if (isset($this->metaMappings[$columnName])) { + $sql .= $this->metaMappings[$columnName]; + } else if (isset($this->discriminatorColumns[$dqlAlias])) { + $sql .= $this->discriminatorColumns[$dqlAlias]; + } + + $sql .= " AS " . $columnName; + } + + return $sql; + } + + /** + * @return string + */ + public function __toString() + { + return $this->generateSelectClause(array()); + } +} 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 00000000000..3dea8de4b2e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php @@ -0,0 +1,2324 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; +use Doctrine\ORM\OptimisticLockException; +use 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 + * @author Fabio B. Silva + * @since 2.0 + * @todo Rename: SQLWalker + */ +class SqlWalker implements TreeWalker +{ + /** + * @var string + */ + const HINT_DISTINCT = 'doctrine.distinct'; + + /** + * @var ResultSetMapping + */ + private $rsm; + + /** + * Counter for generating unique column aliases. + * + * @var integer + */ + private $aliasCounter = 0; + + /** + * Counter for generating unique table aliases. + * + * @var integer + */ + private $tableAliasCounter = 0; + + /** + * Counter for generating unique scalar result. + * + * @var integer + */ + private $scalarResultCounter = 1; + + /** + * Counter for generating unique parameter indexes. + * + * @var integer + */ + private $sqlParamIndex = 0; + + /** + * Counter for generating indexes. + * + * @var integer + */ + private $newObjectCounter = 0; + + /** + * @var ParserResult + */ + private $parserResult; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var \Doctrine\ORM\AbstractQuery + */ + private $query; + + /** + * @var array + */ + private $tableAliasMap = array(); + + /** + * Map from result variable names to their SQL column alias names. + * + * @var array + */ + private $scalarResultAliasMap = array(); + + /** + * Map from Table-Alias + Column-Name to OrderBy-Direction. + * + * @var array + */ + private $orderedColumnsMap = 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. + * + * @var array + */ + private $queryComponents; + + /** + * A list of classes that appear in non-scalar SelectExpressions. + * + * @var array + */ + private $selectedClasses = array(); + + /** + * The DQL alias of the root class of the currently traversed query. + * + * @var array + */ + 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. + * + * @var boolean + */ + private $useSqlTableAliases = true; + + /** + * The database platform abstraction. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * {@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(); + $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Gets the Query instance used by the walker. + * + * @return Query. + */ + public function getQuery() + { + return $this->query; + } + + /** + * Gets the Connection used by the walker. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->conn; + } + + /** + * Gets the EntityManager used by the walker. + * + * @return \Doctrine\ORM\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]; + } + + /** + * {@inheritdoc} + */ + public function getQueryComponents() + { + return $this->queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * {@inheritdoc} + */ + 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] = (preg_match('/[a-z]/i', $tableName[0]) ? strtolower($tableName[0]) : 't') + . $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) + { + return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform); + } + + /** + * 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 ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $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 ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) { + $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql; + } + + /** + * @return string + */ + private function _generateOrderedCollectionOrderByItems() + { + $orderedColumns = array(); + + foreach ($this->selectedClasses as $selectedClass) { + $dqlAlias = $selectedClass['dqlAlias']; + $qComp = $this->queryComponents[$dqlAlias]; + + if ( ! isset($qComp['relation']['orderBy'])) { + continue; + } + + $persister = $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name); + + foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) { + $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform); + $tableName = ($qComp['metadata']->isInheritanceTypeJoined()) + ? $persister->getOwningTable($fieldName) + : $qComp['metadata']->getTableName(); + + $orderedColumn = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName; + + // OrderByClause should replace an ordered relation. see - DDC-2475 + if (isset($this->orderedColumnsMap[$orderedColumn])) { + continue; + } + + $this->orderedColumnsMap[$orderedColumn] = $orientation; + $orderedColumns[] = $orderedColumn . ' ' . $orientation; + } + } + + return implode(', ', $orderedColumns); + } + + /** + * 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) { // discriminators 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); + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + $limit = $this->query->getMaxResults(); + $offset = $this->query->getFirstResult(); + $lockMode = $this->query->getHint(Query::HINT_LOCK_MODE); + $sql = $this->walkSelectClause($AST->selectClause) + . $this->walkFromClause($AST->fromClause) + . $this->walkWhereClause($AST->whereClause); + + if ($AST->groupByClause) { + $sql .= $this->walkGroupByClause($AST->groupByClause); + } + + if ($AST->havingClause) { + $sql .= $this->walkHavingClause($AST->havingClause); + } + + if ($AST->orderByClause) { + $sql .= $this->walkOrderByClause($AST->orderByClause); + } + + if ( ! $AST->orderByClause && ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) { + $sql .= ' ORDER BY ' . $orderBySql; + } + + if ($limit !== null || $offset !== null) { + $sql = $this->platform->modifyLimitQuery($sql, $limit, $offset); + } + + if ($lockMode === null || $lockMode === false || $lockMode === LockMode::NONE) { + return $sql; + } + + if ($lockMode === LockMode::PESSIMISTIC_READ) { + return $sql . ' ' . $this->platform->getReadLockSQL(); + } + + if ($lockMode === LockMode::PESSIMISTIC_WRITE) { + return $sql . ' ' . $this->platform->getWriteLockSQL(); + } + + if ($lockMode !== LockMode::OPTIMISTIC) { + throw QueryException::invalidLockMode(); + } + + foreach ($this->selectedClasses as $selectedClass) { + if ( ! $selectedClass['class']->isVersioned) { + throw OptimisticLockException::lockFailed($selectedClass['class']->name); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + $this->useSqlTableAliases = false; + $this->rsm->isSelect = false; + + return $this->walkUpdateClause($AST->updateClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + $this->useSqlTableAliases = false; + $this->rsm->isSelect = 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 ($this->quoteStrategy->getIdentifierColumnNames($class, $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); + } + + /** + * {@inheritdoc} + */ + 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 .= $this->quoteStrategy->getColumnName($fieldName, $class, $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; + } + + /** + * {@inheritdoc} + */ + 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); + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $type = null; + $isIdentifier = (isset($assoc['id']) && $assoc['id'] === true); + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + if (isset($targetClass->fieldNames[$targetColumn])) { + $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type']; + } + + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, $isIdentifier, $type); + } + } + + // 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; + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + $identificationVarDecls = $fromClause->identificationVariableDeclarations; + $sqlParts = array(); + + foreach ($identificationVarDecls as $identificationVariableDecl) { + $sqlParts[] = $this->walkIdentificationVariableDeclaration($identificationVariableDecl); + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\IdentificationVariableDeclaration $identificationVariableDecl + * + * @return string + */ + public function walkIdentificationVariableDeclaration($identificationVariableDecl) + { + $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration); + + if ($identificationVariableDecl->indexBy) { + $this->walkIndexBy($identificationVariableDecl->indexBy); + } + + foreach ($identificationVariableDecl->joins as $join) { + $sql .= $this->walkJoin($join); + } + + return $sql; + } + + /** + * Walks down a IndexBy AST node. + * + * @param AST\IndexBy $indexBy + * + * @return void + */ + public function walkIndexBy($indexBy) + { + $pathExpression = $indexBy->simpleStateFieldPathExpression; + $alias = $pathExpression->identificationVariable; + $field = $pathExpression->field; + + if (isset($this->scalarFields[$alias][$field])) { + $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]); + + return; + } + + $this->rsm->addIndexBy($alias, $field); + } + + /** + * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\RangeVariableDeclaration $rangeVariableDeclaration + * + * @return string + */ + public function walkRangeVariableDeclaration($rangeVariableDeclaration) + { + $class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName); + $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable; + + if ($rangeVariableDeclaration->isRoot) { + $this->rootAliases[] = $dqlAlias; + } + + $sql = $this->platform->appendLockHint( + $this->quoteStrategy->getTableName($class, $this->platform) . ' ' . + $this->getSQLTableAlias($class->getTableName(), $dqlAlias), + $this->query->getHint(Query::HINT_LOCK_MODE) + ); + + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } + + return $sql; + } + + /** + * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration + * @param int $joinType + * @param AST\ConditionalExpression $condExpr + * + * @return string + * + * @throws QueryException + */ + public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null) + { + $sql = ''; + + $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression; + $joinedDqlAlias = $joinAssociationDeclaration->aliasIdentificationVariable; + $indexBy = $joinAssociationDeclaration->indexBy; + + $relation = $this->queryComponents[$joinedDqlAlias]['relation']; + $targetClass = $this->em->getClassMetadata($relation['targetEntity']); + $sourceClass = $this->em->getClassMetadata($relation['sourceEntity']); + $targetTableName = $this->quoteStrategy->getTableName($targetClass,$this->platform); + + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias); + $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->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); + } + } + + $targetTableJoin = null; + + // 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. + switch (true) { + case ($assoc['type'] & ClassMetadata::TO_ONE): + $conditions = array(); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + if ($relation['isOwningSide']) { + $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn; + + continue; + } + + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn; + } + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $targetTableJoin = array( + 'table' => $targetTableName . ' ' . $targetTableAlias, + 'condition' => implode(' AND ', $conditions), + ); + break; + + case ($assoc['type'] == ClassMetadata::MANY_TO_MANY): + // Join relation table + $joinTable = $assoc['joinTable']; + $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias); + $joinTableName = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform); + + $conditions = array(); + $relationColumns = ($relation['isOwningSide']) + ? $assoc['joinTable']['joinColumns'] + : $assoc['joinTable']['inverseJoinColumns']; + + foreach ($relationColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn; + } + + $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions); + + // Join target table + $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN '; + + $conditions = array(); + $relationColumns = ($relation['isOwningSide']) + ? $assoc['joinTable']['inverseJoinColumns'] + : $assoc['joinTable']['joinColumns']; + + foreach ($relationColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn; + } + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $targetTableJoin = array( + 'table' => $targetTableName . ' ' . $targetTableAlias, + 'condition' => implode(' AND ', $conditions), + ); + break; + + default: + throw new \BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY'); + } + + // Handle WITH clause + $withCondition = (null === $condExpr) ? '' : ('(' . $this->walkConditionalExpression($condExpr) . ')'); + + if ($targetClass->isInheritanceTypeJoined()) { + $ctiJoins = $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias); + // If we have WITH condition, we need to build nested joins for target class table and cti joins + if ($withCondition) { + $sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition']; + } else { + $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins; + } + } else { + $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition']; + } + + if ($withCondition) { + $sql .= ' AND ' . $withCondition; + } + + // Apply the indexes + if ($indexBy) { + // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently. + $this->walkIndexBy($indexBy); + } else if (isset($relation['indexBy'])) { + $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + return $function->getSql($this); + } + + /** + * {@inheritdoc} + */ + 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); + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + $type = strtoupper($orderByItem->type); + $expr = $orderByItem->expression; + $sql = ($expr instanceof AST\Node) + ? $expr->dispatch($this) + : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']); + + $this->orderedColumnsMap[$sql] = $type; + + if ($expr instanceof AST\Subselect) { + return '(' . $sql . ') ' . $type; + } + + return $sql . ' ' . $type; + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression); + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + $joinType = $join->joinType; + $joinDeclaration = $join->joinAssociationDeclaration; + + $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) + ? ' LEFT JOIN ' + : ' INNER JOIN '; + + switch (true) { + case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration): + $class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName); + $dqlAlias = $joinDeclaration->aliasIdentificationVariable; + $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $condition = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')'; + $condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER) + ? ' AND ' + : ' ON '; + + $sql .= $this->walkRangeVariableDeclaration($joinDeclaration); + + $conditions = array($condition); + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($dqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $sql .= $condExprConjunction . implode(' AND ', $conditions); + break; + + case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration): + $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression); + break; + } + + return $sql; + } + + /** + * Walks down a CaseExpression AST node and generates the corresponding SQL. + * + * @param AST\CoalesceExpression|AST\NullIfExpression|AST\GeneralCaseExpression|AST\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 AST\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 AST\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 AST\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 AST\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; + } + + /** + * {@inheritdoc} + */ + 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); + } + + $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 = $this->quoteStrategy->getColumnName($fieldName, $class, $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\ParenthesisExpression): + 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; + + case ($expr instanceof AST\NewObjectExpression): + $sql .= $this->walkNewObject($expr,$selectExpression->fieldIdentificationVariable); + 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 = $this->quoteStrategy->getColumnName($fieldName, $class, $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 = $this->quoteStrategy->getColumnName($fieldName, $subClass, $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; + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')'; + } + + /** + * {@inheritdoc} + */ + 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; + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations; + $sqlParts = array (); + + foreach ($identificationVarDecls as $subselectIdVarDecl) { + $sqlParts[] = $this->walkIdentificationVariableDeclaration($subselectIdVarDecl); + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '') + . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression); + } + + /** + * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression + * + * @return string. + */ + public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression) + { + return sprintf('(%s)', $parenthesisExpression->expression->dispatch($this)); + } + + /** + * @param AST\NewObjectExpression $newObjectExpression + * + * @return string The SQL. + */ + public function walkNewObject($newObjectExpression, $newObjectResultAlias=null) + { + $sqlSelectExpressions = array(); + $objIndex = $newObjectResultAlias?:$this->newObjectCounter++; + + foreach ($newObjectExpression->args as $argIndex => $e) { + $resultAlias = $this->scalarResultCounter++; + $columnAlias = $this->getSQLColumnAlias('sclr'); + $fieldType = 'string'; + + switch (true) { + case ($e instanceof AST\NewObjectExpression): + $sqlSelectExpressions[] = $e->dispatch($this); + break; + + case ($e instanceof AST\Subselect): + $sqlSelectExpressions[] = '(' . $e->dispatch($this) . ') AS ' . $columnAlias; + break; + + case ($e instanceof AST\PathExpression): + $fieldName = $e->field; + $dqlAlias = $e->identificationVariable; + $qComp = $this->queryComponents[$dqlAlias]; + $class = $qComp['metadata']; + $fieldType = $class->getTypeOfField($fieldName); + + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; + break; + + case ($e instanceof AST\Literal): + switch ($e->type) { + case AST\Literal::BOOLEAN: + $fieldType = 'boolean'; + break; + + case AST\Literal::NUMERIC: + $fieldType = is_float($e->value) ? 'float' : 'integer'; + break; + } + + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; + break; + + default: + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; + break; + } + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); + + $this->rsm->newObjectMappings[$columnAlias] = array( + 'className' => $newObjectExpression->className, + 'objIndex' => $objIndex, + 'argIndex' => $argIndex + ); + } + + return implode(', ', $sqlSelectExpressions); + } + + /** + * {@inheritdoc} + */ + 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\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; + + case ($expr instanceof AST\ParenthesisExpression): + $sql .= $this->walkParenthesisExpression($expr); + break; + + default: // IdentificationVariable + $sql .= $this->walkEntityIdentificationVariable($expr); + break; + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '') + . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + $sqlParts = array(); + + foreach ($groupByClause->groupByItems as $groupByItem) { + $sqlParts[] = $this->walkGroupByItem($groupByItem); + } + + return ' GROUP BY ' . implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + // StateFieldPathExpression + if ( ! is_string($groupByItem)) { + return $this->walkPathExpression($groupByItem); + } + + // ResultVariable + if (isset($this->queryComponents[$groupByItem]['resultVariable'])) { + $resultVariable = $this->queryComponents[$groupByItem]['resultVariable']; + + if ($resultVariable instanceof AST\PathExpression) { + return $this->walkPathExpression($resultVariable); + } + + if (isset($resultVariable->pathExpression)) { + return $this->walkPathExpression($resultVariable->pathExpression); + } + + 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); + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform); + + $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable); + $this->rootAliases[] = $deleteClause->aliasIdentificationVariable; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + $class = $this->em->getClassMetadata($updateClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'UPDATE ' . $this->quoteStrategy->getTableName($class, $this->platform); + + $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable); + $this->rootAliases[] = $updateClause->aliasIdentificationVariable; + + $sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + 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; + } + + /** + * {@inheritdoc} + */ + 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 = '(' . $condSql . ') AND '; + } + + $condSql .= implode(' AND ', $filterClauses); + } + } + + if ($condSql) { + return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql); + } + + if ($discrSql) { + return ' WHERE ' . $discrSql; + } + + return ''; + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + // Phase 2 AST optimization: Skip processing 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)); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + // Phase 2 AST optimization: Skip processing 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)); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + // Phase 2 AST optimization: Skip processing of ConditionalFactor + // if only one ConditionalPrimary is defined + return ( ! ($factor instanceof AST\ConditionalFactor)) + ? $this->walkConditionalPrimary($factor) + : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($primary) + { + if ($primary->isSimpleConditionalExpression()) { + return $primary->simpleConditionalExpression->dispatch($this); + } + + if ($primary->isConditionalExpression()) { + $condExpr = $primary->conditionalExpression; + + return '(' . $this->walkConditionalExpression($condExpr) . ')'; + } + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + $sql = ($existsExpr->not) ? 'NOT ' : ''; + + $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + 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; + $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 .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + $sqlParts = array(); + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform); + + $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn; + } + + foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $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 .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias + . ' INNER JOIN ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' ON '; + + // join conditions + $joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns']; + $joinSqlParts = array(); + + foreach ($joinColumns as $joinColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $targetClass, $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 = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class, $this->platform); + + $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn; + } + + foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) { + if (isset($dqlParamKey)) { + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + } + + $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' IN (' . $entitySql . ')'; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + $sizeFunc = new AST\Functions\SizeFunction('size'); + $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression; + + return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0'); + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + $expression = $nullCompExpr->expression; + $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + + // Handle ResultVariable + if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) { + return $this->walkResultVariable($expression) . $comparison; + } + + // Handle InputParameter mapping inclusion to ParserResult + if ($expression instanceof AST\InputParameter) { + return $this->walkInputParameter($expression) . $comparison; + } + + return $expression->dispatch($this) . $comparison; + } + + /** + * {@inheritdoc} + */ + 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; + } + + /** + * {@inheritdoc} + */ + public function walkInstanceOfExpression($instanceOfExpr) + { + $sql = ''; + + $dqlAlias = $instanceOfExpr->identificationVariable; + $discrClass = $class = $this->queryComponents[$dqlAlias]['metadata']; + + 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) { + $this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue'); + + $sqlParameterList[] = $this->walkInputParameter($parameter); + + continue; + } + + // Get name from ClassMetadata to resolve aliases. + $entityClassName = $this->em->getClassMetadata($parameter)->name; + $discriminatorValue = $class->discriminatorValue; + + if ($entityClassName !== $class->name) { + $discrMap = array_flip($class->discriminatorMap); + + if ( ! isset($discrMap[$entityClassName])) { + throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + } + + $discriminatorValue = $discrMap[$entityClassName]; + } + + $sqlParameterList[] = $this->conn->quote($discriminatorValue); + } + + $sql .= '(' . implode(', ', $sqlParameterList) . ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkInParameter($inParam) + { + return $inParam instanceof AST\InputParameter + ? $this->walkInputParameter($inParam) + : $this->walkLiteral($inParam); + } + + /** + * {@inheritdoc} + */ + 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); + } + } + + /** + * {@inheritdoc} + */ + 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; + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + $stringExpr = $likeExpr->stringExpression; + $leftExpr = (is_string($stringExpr) && isset($this->queryComponents[$stringExpr]['resultVariable'])) + ? $this->walkResultVariable($stringExpr) + : $stringExpr->dispatch($this); + + $sql = $leftExpr . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; + + if ($likeExpr->stringPattern instanceof AST\InputParameter) { + $sql .= $this->walkInputParameter($likeExpr->stringPattern); + } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode) { + $sql .= $this->walkFunction($likeExpr->stringPattern); + } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) { + $sql .= $this->walkPathExpression($likeExpr->stringPattern); + } else { + $sql .= $this->walkLiteral($likeExpr->stringPattern); + } + + if ($likeExpr->escapeChar) { + $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + return $this->walkPathExpression($stateFieldPathExpression); + } + + /** + * {@inheritdoc} + */ + 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; + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++); + + $parameter = $this->query->getParameter($inputParam->name); + + if ($parameter && Type::hasType($type = $parameter->getType())) { + return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform); + } + + return '?'; + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + return ($arithmeticExpr->isSimpleArithmeticExpression()) + ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression) + : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) { + return $this->walkArithmeticTerm($simpleArithmeticExpr); + } + + return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)); + } + + /** + * {@inheritdoc} + */ + 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 processing 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)); + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + if (is_string($factor)) { + return $factor; + } + + // Phase 2 AST optimization: Skip processing 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 $primary + * + * @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); + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + return (is_string($stringPrimary)) + ? $this->conn->quote($stringPrimary) + : $stringPrimary->dispatch($this); + } + + /** + * {@inheritdoc} + */ + 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 00000000000..9ddd86b0432 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php @@ -0,0 +1,479 @@ +. + */ + +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 \Doctrine\ORM\AbstractQuery $query The parsed Query. + * @param \Doctrine\ORM\Query\ParserResult $parserResult The result of the parsing process. + * @param array $queryComponents The query components (symbol table). + */ + public function __construct($query, $parserResult, array $queryComponents); + + /** + * Returns internal queryComponents array. + * + * @return array + */ + public function getQueryComponents(); + + /** + * Sets or overrides a query component for a given dql alias. + * + * @param string $dqlAlias The DQL alias. + * @param array $queryComponent + * + * @return void + */ + public function setQueryComponent($dqlAlias, array $queryComponent); + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\SelectStatement $AST + * + * @return string The SQL. + */ + function walkSelectStatement(AST\SelectStatement $AST); + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SelectClause $selectClause + * + * @return string The SQL. + */ + function walkSelectClause($selectClause); + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @param AST\FromClause $fromClause + * + * @return string The SQL. + */ + function walkFromClause($fromClause); + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @param AST\Functions\FunctionNode $function + * + * @return string The SQL. + */ + function walkFunction($function); + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param AST\OrderByClause $orderByClause + * + * @return string The SQL. + */ + function walkOrderByClause($orderByClause); + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param AST\OrderByItem $orderByItem + * + * @return string The SQL. + */ + function walkOrderByItem($orderByItem); + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param AST\HavingClause $havingClause + * + * @return string The SQL. + */ + function walkHavingClause($havingClause); + + /** + * Walks down a Join AST node and creates the corresponding SQL. + * + * @param AST\Join $join + * + * @return string The SQL. + */ + function walkJoin($join); + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param AST\SelectExpression $selectExpression + * + * @return string The SQL. + */ + function walkSelectExpression($selectExpression); + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\QuantifiedExpression $qExpr + * + * @return string The SQL. + */ + function walkQuantifiedExpression($qExpr); + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param AST\Subselect $subselect + * + * @return string The SQL. + */ + function walkSubselect($subselect); + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SubselectFromClause $subselectFromClause + * + * @return string The SQL. + */ + function walkSubselectFromClause($subselectFromClause); + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleSelectClause $simpleSelectClause + * + * @return string The SQL. + */ + function walkSimpleSelectClause($simpleSelectClause); + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleSelectExpression $simpleSelectExpression + * + * @return string The SQL. + */ + function walkSimpleSelectExpression($simpleSelectExpression); + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\AggregateExpression $aggExpression + * + * @return string The SQL. + */ + function walkAggregateExpression($aggExpression); + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param AST\GroupByClause $groupByClause + * + * @return string The SQL. + */ + function walkGroupByClause($groupByClause); + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param AST\PathExpression|string $groupByItem + * + * @return string The SQL. + */ + function walkGroupByItem($groupByItem); + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateStatement $AST + * + * @return string The SQL. + */ + function walkUpdateStatement(AST\UpdateStatement $AST); + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\DeleteStatement $AST + * + * @return string The SQL. + */ + function walkDeleteStatement(AST\DeleteStatement $AST); + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param AST\DeleteClause $deleteClause + * + * @return string The SQL. + */ + function walkDeleteClause(AST\DeleteClause $deleteClause); + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateClause $updateClause + * + * @return string The SQL. + */ + function walkUpdateClause($updateClause); + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateItem $updateItem + * + * @return string The SQL. + */ + function walkUpdateItem($updateItem); + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * WhereClause or not, the appropriate discriminator sql is added. + * + * @param AST\WhereClause $whereClause + * + * @return string The SQL. + */ + function walkWhereClause($whereClause); + + /** + * Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalExpression $condExpr + * + * @return string The SQL. + */ + function walkConditionalExpression($condExpr); + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalTerm $condTerm + * + * @return string The SQL. + */ + function walkConditionalTerm($condTerm); + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalFactor $factor + * + * @return string The SQL. + */ + function walkConditionalFactor($factor); + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalPrimary $primary + * + * @return string The SQL. + */ + function walkConditionalPrimary($primary); + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ExistsExpression $existsExpr + * + * @return string The SQL. + */ + function walkExistsExpression($existsExpr); + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\CollectionMemberExpression $collMemberExpr + * + * @return string The SQL. + */ + function walkCollectionMemberExpression($collMemberExpr); + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\EmptyCollectionComparisonExpression $emptyCollCompExpr + * + * @return string The SQL. + */ + function walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\NullComparisonExpression $nullCompExpr + * + * @return string The SQL. + */ + function walkNullComparisonExpression($nullCompExpr); + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\InExpression $inExpr + * + * @return string The SQL. + */ + function walkInExpression($inExpr); + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\InstanceOfExpression $instanceOfExpr + * + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr); + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $literal + * + * @return string The SQL. + */ + function walkLiteral($literal); + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\BetweenExpression $betweenExpr + * + * @return string The SQL. + */ + function walkBetweenExpression($betweenExpr); + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\LikeExpression $likeExpr + * + * @return string The SQL. + */ + function walkLikeExpression($likeExpr); + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\PathExpression $stateFieldPathExpression + * + * @return string The SQL. + */ + function walkStateFieldPathExpression($stateFieldPathExpression); + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ComparisonExpression $compExpr + * + * @return string The SQL. + */ + function walkComparisonExpression($compExpr); + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param AST\InputParameter $inputParam + * + * @return string The SQL. + */ + function walkInputParameter($inputParam); + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ArithmeticExpression $arithmeticExpr + * + * @return string The SQL. + */ + function walkArithmeticExpression($arithmeticExpr); + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed $term + * + * @return string The SQL. + */ + function walkArithmeticTerm($term); + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $stringPrimary + * + * @return string The SQL. + */ + function walkStringPrimary($stringPrimary); + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $factor + * + * @return string The SQL. + */ + function walkArithmeticFactor($factor); + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleArithmeticExpression $simpleArithmeticExpr + * + * @return string The SQL. + */ + function walkSimpleArithmeticExpression($simpleArithmeticExpr); + + /** + * Walks down a PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed $pathExpr + * + * @return string The SQL. + */ + function walkPathExpression($pathExpr); + + /** + * Walks down a ResultVariable that represents an 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. + * + * @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST + * + * @return Exec\AbstractSqlExecutor + */ + function getExecutor($AST); +} 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 00000000000..e95155c13f3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php @@ -0,0 +1,440 @@ +. + */ + +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 +{ + /** + * The original Query. + * + * @var \Doctrine\ORM\AbstractQuery + */ + private $_query; + + /** + * The ParserResult of the original query that was produced by the Parser. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The query components of the original query (the "symbol table") that was produced by the Parser. + * + * @var array + */ + private $_queryComponents; + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * {@inheritdoc} + */ + public function getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->_queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * @return array + */ + protected function _getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * Retrieves the Query Instance responsible for the current walkers execution. + * + * @return \Doctrine\ORM\AbstractQuery + */ + protected function _getQuery() + { + return $this->_query; + } + + /** + * Retrieves the ParserResult. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + protected function _getParserResult() + { + return $this->_parserResult; + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($primary) + { + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + } + + /** + * {@inheritdoc} + */ + function walkInstanceOfExpression($instanceOfExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + } +} 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 00000000000..19597ed1a3d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php @@ -0,0 +1,575 @@ +. + */ + +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. + * + * @var TreeWalker[] + */ + private $_walkers; + + /** + * The original Query. + * + * @var \Doctrine\ORM\AbstractQuery + */ + private $_query; + + /** + * The ParserResult of the original query that was produced by the Parser. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The query components of the original query (the "symbol table") that was produced by the Parser. + * + * @var array + */ + private $_queryComponents; + + /** + * Returns the internal queryComponents array. + * + * @return array + */ + public function getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->_queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + $this->_walkers = new TreeWalkerChainIterator($this, $query, $parserResult); + } + + /** + * Adds a tree walker to the chain. + * + * @param string $walkerClass The class of the walker to instantiate. + * + * @return void + */ + public function addTreeWalker($walkerClass) + { + $this->_walkers[] = $walkerClass; + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectStatement($AST); + + $this->_queryComponents = $walker->getQueryComponents(); + } + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectClause($selectClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkFromClause($fromClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + foreach ($this->_walkers as $walker) { + $walker->walkFunction($function); + } + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByClause($orderByClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByItem($orderByItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkHavingClause($havingClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + foreach ($this->_walkers as $walker) { + $walker->walkJoin($join); + } + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectExpression($selectExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkQuantifiedExpression($qExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselect($subselect); + } + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselectFromClause($subselectFromClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectClause($simpleSelectClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectExpression($simpleSelectExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkAggregateExpression($aggExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByClause($groupByClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByItem($groupByItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateStatement($AST); + } + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteStatement($AST); + } + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteClause($deleteClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateClause($updateClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateItem($updateItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkWhereClause($whereClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalExpression($condExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalTerm($condTerm); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalFactor($factor); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($condPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalPrimary($condPrimary); + } + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkExistsExpression($existsExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkCollectionMemberExpression($collMemberExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkNullComparisonExpression($nullCompExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInExpression($inExpr); + } + } + + /** + * {@inheritdoc} + */ + function walkInstanceOfExpression($instanceOfExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInstanceOfExpression($instanceOfExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + foreach ($this->_walkers as $walker) { + $walker->walkLiteral($literal); + } + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkBetweenExpression($betweenExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkLikeExpression($likeExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkStateFieldPathExpression($stateFieldPathExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkComparisonExpression($compExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + foreach ($this->_walkers as $walker) { + $walker->walkInputParameter($inputParam); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticExpression($arithmeticExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticTerm($term); + } + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkStringPrimary($stringPrimary); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticFactor($factor); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleArithmeticExpression($simpleArithmeticExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkPathExpression($pathExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + foreach ($this->_walkers as $walker) { + $walker->walkResultVariable($resultVariable); + } + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChainIterator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChainIterator.php new file mode 100644 index 00000000000..1b0fb8a4f2e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChainIterator.php @@ -0,0 +1,139 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * + */ +class TreeWalkerChainIterator implements \Iterator, \ArrayAccess +{ + /** + * @var TreeWalker[] + */ + private $walkers = array(); + /** + * @var TreeWalkerChain + */ + private $treeWalkerChain; + /** + * @var + */ + private $query; + /** + * @var + */ + private $parserResult; + + public function __construct(TreeWalkerChain $treeWalkerChain, $query, $parserResult) + { + $this->treeWalkerChain = $treeWalkerChain; + $this->query = $query; + $this->parserResult = $parserResult; + } + + /** + * {@inheritdoc} + */ + function rewind() + { + return reset($this->walkers); + } + + /** + * {@inheritdoc} + */ + function current() + { + return $this->offsetGet(key($this->walkers)); + } + + /** + * {@inheritdoc} + */ + function key() + { + return key($this->walkers); + } + + /** + * {@inheritdoc} + */ + function next() + { + next($this->walkers); + + return $this->offsetGet(key($this->walkers)); + } + + /** + * {@inheritdoc} + */ + function valid() + { + return key($this->walkers) !== null; + } + + + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + return isset($this->walkers[$offset]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + if ($this->offsetExists($offset)) { + return new $this->walkers[$offset]( + $this->query, + $this->parserResult, + $this->treeWalkerChain->getQueryComponents() + ); + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value) + { + if (is_null($offset)) { + $this->walkers[] = $value; + } else { + $this->walkers[$offset] = $value; + } + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + if ($this->offsetExists($offset)) { + unset($this->walkers[$offset]); + } + } +} \ 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 00000000000..e7c603dc876 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php @@ -0,0 +1,1516 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; + +use Doctrine\ORM\Query\Expr; +use Doctrine\ORM\Query\QueryExpressionVisitor; + +/** + * 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; + + /** + * The EntityManager used by this QueryBuilder. + * + * @var EntityManagerInterface + */ + private $_em; + + /** + * The array of DQL parts collected. + * + * @var array + */ + private $_dqlParts = array( + 'distinct' => false, + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array() + ); + + /** + * The type of query this is. Can be select, update or delete. + * + * @var integer + */ + private $_type = self::SELECT; + + /** + * The state of the query object. Can be dirty or clean. + * + * @var integer + */ + private $_state = self::STATE_CLEAN; + + /** + * The complete DQL string for this query. + * + * @var string + */ + private $_dql; + + /** + * The query parameters. + * + * @var \Doctrine\Common\Collections\ArrayCollection + */ + private $parameters; + + /** + * The index of the first result to retrieve. + * + * @var integer + */ + private $_firstResult = null; + + /** + * The maximum number of results to retrieve. + * + * @var integer + */ + private $_maxResults = null; + + /** + * Keeps root entity alias names for join entities. + * + * @var array + */ + private $joinRootAliases = array(); + + /** + * Whether to use second level cache, if available. + * + * @var boolean + */ + protected $cacheable = false; + + /** + * Second level cache region name. + * + * @var string|null + */ + protected $cacheRegion; + + /** + * Second level query cache mode. + * + * @var integer|null + */ + protected $cacheMode; + + /** + * @var integer + */ + protected $lifetime = 0; + + /** + * Initializes a new QueryBuilder that uses the given EntityManager. + * + * @param EntityManagerInterface $em The EntityManager to use. + */ + public function __construct(EntityManagerInterface $em) + { + $this->_em = $em; + $this->parameters = new ArrayCollection(); + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $em->createQueryBuilder(); + * $qb + * ->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(); + } + + /** + * + * Enable/disable second level query (result) caching for this query. + * + * @param boolean $cacheable + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setCacheable($cacheable) + { + $this->cacheable = (boolean) $cacheable; + + return $this; + } + + /** + * @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise. + */ + public function isCacheable() + { + return $this->cacheable; + } + + /** + * @param string $cacheRegion + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setCacheRegion($cacheRegion) + { + $this->cacheRegion = (string) $cacheRegion; + + return $this; + } + + /** + * Obtain the name of the second level query cache region in which query results will be stored + * + * @return The cache region name; NULL indicates the default region. + */ + public function getCacheRegion() + { + return $this->cacheRegion; + } + + /** + * @return integer + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * Sets the life-time for this query into second level cache. + * + * @param integer $lifetime + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setLifetime($lifetime) + { + $this->lifetime = (integer) $lifetime; + + return $this; + } + + /** + * @return integer + */ + public function getCacheMode() + { + return $this->cacheMode; + } + + /** + * @param integer $cacheMode + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setCacheMode($cacheMode) + { + $this->cacheMode = (integer) $cacheMode; + + return $this; + } + + /** + * Gets the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->_type; + } + + /** + * Gets the associated EntityManager for this query builder. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Gets the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->_state; + } + + /** + * Gets 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; + } + + 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() + { + $parameters = clone $this->parameters; + $query = $this->_em->createQuery($this->getDQL()) + ->setParameters($parameters) + ->setFirstResult($this->_firstResult) + ->setMaxResults($this->_maxResults); + + if ($this->lifetime) { + $query->setLifetime($this->lifetime); + } + + if ($this->cacheMode) { + $query->setCacheMode($this->cacheMode); + } + + if ($this->cacheable) { + $query->setCacheable($this->cacheable); + } + + if ($this->cacheRegion) { + $query->setCacheRegion($this->cacheRegion); + } + + return $query; + } + + /** + * Finds the root entity alias of the joined entity. + * + * @param string $alias The alias of the new join entity + * @param string $parentAlias The parent entity alias of the join relationship + * + * @return string + */ + private function findRootAlias($alias, $parentAlias) + { + $rootAlias = null; + + if (in_array($parentAlias, $this->getRootAliases())) { + $rootAlias = $parentAlias; + } elseif (isset($this->joinRootAliases[$parentAlias])) { + $rootAlias = $this->joinRootAliases[$parentAlias]; + } else { + // Should never happen with correct joining order. Might be + // thoughtful to throw exception instead. + $rootAlias = $this->getRootAlias(); + } + + $this->joinRootAliases[$alias] = $rootAlias; + + return $rootAlias; + } + + /** + * 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. + * @throws RuntimeException + * + * @return string + */ + public function getRootAlias() + { + $aliases = $this->getRootAliases(); + + if ( ! isset($aliases[0])) { + throw new \RuntimeException('No alias was set before invoking getRootAlias().'); + } + + 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 + */ + 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 all the aliases that have been used in the query. + * Including all select root aliases and join aliases + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->join('u.articles','a'; + * + * $qb->getAllAliases(); // array('u','a') + * + * @return array + */ + public function getAllAliases() { + return array_merge($this->getRootAliases(),array_keys($this->joinRootAliases)); + } + + /** + * 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 + */ + 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) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + if (count($filteredParameters)) { + $parameter = $filteredParameters->first(); + $parameter->setValue($value, $type); + + return $this; + } + + $parameter = new Query\Parameter($key, $value, $type); + + $this->parameters->add($parameter); + + 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(new ArrayCollection(array( + * new Parameter('user_id1', 1), + * new Parameter('user_id2', 2) + * ))); + * + * + * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters to set. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameters($parameters) + { + // BC compatibility with 2.3- + if (is_array($parameters)) { + $parameterCollection = new ArrayCollection(); + + foreach ($parameters as $key => $value) { + $parameter = new Query\Parameter($key, $value); + + $parameterCollection->add($parameter); + } + + $parameters = $parameterCollection; + } + + $this->parameters = $parameters; + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed. + * + * @return \Doctrine\Common\Collections\ArrayCollection The currently defined query parameters. + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return Query\Parameter|null The value of the bound parameter. + */ + public function getParameter($key) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + return count($filteredParameters) ? $filteredParameters->first() : 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 Expr\Base $dqlPart + * @param bool $append + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function add($dqlPartName, $dqlPart, $append = false) + { + if ($append && ($dqlPartName === "where" || $dqlPartName === "having")) { + throw new \InvalidArgumentException( + "Using \$append = true does not have an effect with 'where' or 'having' ". + "parts. See QueryBuilder#andWhere() for an example for correct usage." + ); + } + + $isMultiple = is_array($this->_dqlParts[$dqlPartName]) + && !($dqlPartName == 'join' && !$append); + + // Allow adding any part retrieved from self::getDQLParts(). + if (is_array($dqlPart) && $dqlPartName != 'join') { + $dqlPart = reset($dqlPart); + } + + // This is introduced for backwards compatibility reasons. + // TODO: Remove for 3.0 + if ($dqlPartName == 'join') { + $newDqlPart = array(); + + foreach ($dqlPart as $k => $v) { + $k = is_numeric($k) ? $this->getRootAlias() : $k; + + $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); + } + + /** + * Adds a DISTINCT flag to this query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->distinct() + * ->from('User', 'u'); + * + * + * @param bool $flag + * + * @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)); + } + + /** + * Creates and adds 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); + } + + /** + * Updates a query root corresponding to an entity setting its index by. This method is intended to be used with + * EntityRepository->createQueryBuilder(), which creates the initial FROM clause and do not allow you to update it + * setting an index by. + * + * + * $qb = $userRepository->createQueryBuilder('u') + * ->indexBy('u', 'u.id'); + * + * // Is equivalent to... + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u', 'u.id'); + * + * + * @param string $alias The root alias of the class. + * @param string $indexBy The index for the from. + * + * @return QueryBuilder This QueryBuilder instance. + * + * @throws Query\QueryException + */ + public function indexBy($alias, $indexBy) + { + $rootAliases = $this->getRootAliases(); + + if (!in_array($alias, $rootAliases)) { + throw new Query\QueryException( + sprintf('Specified root alias %s must be set before invoking indexBy().', $alias) + ); + } + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if ($fromClause->getAlias() !== $alias) { + continue; + } + + $fromClause = new Expr\From($fromClause->getFrom(), $fromClause->getAlias(), $indexBy); + } + + return $this; + } + + /** + * 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|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $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|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $parentAlias = substr($join, 0, strpos($join, '.')); + + $rootAlias = $this->findRootAlias($alias, $parentAlias); + + $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|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $parentAlias = substr($join, 0, strpos($join, '.')); + + $rootAlias = $this->findRootAlias($alias, $parentAlias); + + $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() + { + $args = func_get_args(); + $where = $this->getDQLPart('where'); + + if ($where instanceof Expr\Andx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Andx($args); + } + + return $this->add('where', $where); + } + + /** + * 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 + * + * @see where() + */ + public function orWhere() + { + $args = func_get_args(); + $where = $this->getDqlPart('where'); + + if ($where instanceof Expr\Orx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Orx($args); + } + + return $this->add('where', $where); + } + + /** + * 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) + { + $args = func_get_args(); + $having = $this->getDqlPart('having'); + + 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) + { + $args = func_get_args(); + $having = $this->getDqlPart('having'); + + 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|Expr\OrderBy $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); + + return $this->add('orderBy', $orderBy); + } + + /** + * Adds an ordering to the query results. + * + * @param string|Expr\OrderBy $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); + + return $this->add('orderBy', $orderBy, true); + } + + /** + * Adds criteria to the query. + * + * Adds where expressions with AND operator. + * Adds orderings. + * Overrides firstResult and maxResults if they're set. + * + * @param Criteria $criteria + * @return QueryBuilder + * @throws Query\QueryException + */ + public function addCriteria(Criteria $criteria) + { + $allAliases = $this->getAllAliases(); + if ( ! isset($allAliases[0])) { + throw new Query\QueryException('No aliases are set before invoking addCriteria().'); + } + + $visitor = new QueryExpressionVisitor($this->getAllAliases()); + + if ($whereExpression = $criteria->getWhereExpression()) { + $this->andWhere($visitor->dispatch($whereExpression)); + foreach ($visitor->getParameters() as $parameter) { + $this->parameters->add($parameter); + } + } + + if ($criteria->getOrderings()) { + foreach ($criteria->getOrderings() as $sort => $order) { + + $hasValidAlias = false; + foreach($allAliases as $alias) { + if(strpos($sort . '.', $alias . '.') === 0) { + $hasValidAlias = true; + break; + } + } + + if(!$hasValidAlias) { + $sort = $allAliases[0] . '.' . $sort; + } + + $this->addOrderBy($sort, $order); + } + } + + // Overwrite limits only if they was set in criteria + if (($firstResult = $criteria->getFirstResult()) !== null) { + $this->setFirstResult($firstResult); + } + if (($maxResults = $criteria->getMaxResults()) !== null) { + $this->setMaxResults($maxResults); + } + + return $this; + } + + /** + * Gets 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]; + } + + /** + * Gets all query parts. + * + * @return array $dqlParts + * + * @todo Rename: getQueryParts (or remove?) + */ + public function getDQLParts() + { + return $this->_dqlParts; + } + + /** + * @return string + */ + 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' => ', ')); + } + + /** + * @return string + */ + 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' => ', ')); + } + + /** + * @return string + */ + 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; + } + + /** + * @param string $queryPartName + * @param array $options + * + * @return string + */ + 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'] : ''); + } + + /** + * Resets DQL parts. + * + * @param array|null $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; + } + + /** + * Resets single DQL part. + * + * @param string $part + * + * @return QueryBuilder + */ + public function resetDQLPart($part) + { + $this->_dqlParts[$part] = is_array($this->_dqlParts[$part]) ? array() : 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 clones 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; + } + } + + $parameters = array(); + + foreach ($this->parameters as $parameter) { + $parameters[] = clone $parameter; + } + + $this->parameters = new ArrayCollection($parameters); + } +} 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 00000000000..e69de29bb2d diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php new file mode 100644 index 00000000000..12163eff615 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\ORM\Repository; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * This factory is used to create default repository objects for entities at runtime. + * + * @author Guilherme Blanco + * @since 2.4 + */ +final class DefaultRepositoryFactory implements RepositoryFactory +{ + /** + * The list of EntityRepository instances. + * + * @var \Doctrine\Common\Persistence\ObjectRepository[] + */ + private $repositoryList = array(); + + /** + * {@inheritdoc} + */ + public function getRepository(EntityManagerInterface $entityManager, $entityName) + { + $repositoryHash = $entityManager->getClassMetadata($entityName)->getName() . spl_object_hash($entityManager); + + if (isset($this->repositoryList[$repositoryHash])) { + return $this->repositoryList[$repositoryHash]; + } + + return $this->repositoryList[$repositoryHash] = $this->createRepository($entityManager, $entityName); + } + + /** + * Create a new repository instance for an entity class. + * + * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. + * @param string $entityName The name of the entity. + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + private function createRepository(EntityManagerInterface $entityManager, $entityName) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */ + $metadata = $entityManager->getClassMetadata($entityName); + $repositoryClassName = $metadata->customRepositoryClassName + ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName(); + + return new $repositoryClassName($entityManager, $metadata); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/RepositoryFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/RepositoryFactory.php new file mode 100644 index 00000000000..f3af43ebe0d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/RepositoryFactory.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Repository; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Interface for entity repository factory. + * + * @author Guilherme Blanco + * @since 2.4 + */ +interface RepositoryFactory +{ + /** + * Gets the repository for an entity class. + * + * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. + * @param string $entityName The name of the entity. + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + public function getRepository(EntityManagerInterface $entityManager, $entityName); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/AttachEntityListenersListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/AttachEntityListenersListener.php new file mode 100644 index 00000000000..2b6c91aedcf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/AttachEntityListenersListener.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; + +/** + * Mechanism to programmatically attach entity listeners. + * + * @author Fabio B. SIlva + * + * @since 2.5 + */ +class AttachEntityListenersListener +{ + /** + * @var array[] + */ + private $entityListeners = array(); + + /** + * Adds a entity listener for a specific entity. + * + * @param string $entityClass The entity to attach the listener. + * @param string $listenerClass The listener class. + * @param string $eventName The entity lifecycle event. + * @param string $listenerCallback|null The listener callback method or NULL to use $eventName. + * + * @return void + */ + public function addEntityListener($entityClass, $listenerClass, $eventName, $listenerCallback = null) + { + $this->entityListeners[ltrim($entityClass, '\\')][] = array( + 'event' => $eventName, + 'class' => $listenerClass, + 'method' => $listenerCallback ?: $eventName + ); + } + + /** + * Processes event and attach the entity listener. + * + * @param \Doctrine\ORM\Event\LoadClassMetadataEventArgs $event + * + * @return void + */ + public function loadClassMetadata(LoadClassMetadataEventArgs $event) + { + /** @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */ + $metadata = $event->getClassMetadata(); + + if ( ! isset($this->entityListeners[$metadata->name])) { + return; + } + + foreach ($this->entityListeners[$metadata->name] as $listener) { + $metadata->addEntityListener($listener['event'], $listener['class'], $listener['method']); + } + + unset($this->entityListeners[$metadata->name]); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php new file mode 100644 index 00000000000..80df37a6400 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache; + +/** + * Command to clear a collection cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CollectionRegionCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:region:collection') + ->setDescription('Clear a second-level cache collection region.') + ->addArgument('owner-class', InputArgument::OPTIONAL, 'The owner entity name.') + ->addArgument('association', InputArgument::OPTIONAL, 'The association collection name.') + ->addArgument('owner-id', InputArgument::OPTIONAL, 'The owner identifier.') + ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all entity regions will be deleted/invalidated.') + ->addOption('flush', null, InputOption::VALUE_NONE,'If defined, all cache entries will be flushed.'); + + + $this->setHelp(<<%command.name% command is meant to clear a second-level cache collection regions for an associated Entity Manager. +It is possible to delete/invalidate all collection region, a specific collection region or flushes the cache provider. + +The execution type differ on how you execute the command. +If you want to invalidate all entries for an collection region this command would do the work: + +%command.name% 'Entities\MyEntity' 'collectionName' + +To invalidate a specific entry you should use : + +%command.name% 'Entities\MyEntity' 'collectionName' 1 + +If you want to invalidate all entries for the all collection regions: + +%command.name% --all + +Alternatively, if you want to flush the configured cache provider for an collection region use this command: + +%command.name% 'Entities\MyEntity' 'collectionName' --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 + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $ownerClass = $input->getArgument('owner-class'); + $assoc = $input->getArgument('association'); + $ownerId = $input->getArgument('owner-id'); + $cache = $em->getCache(); + + if ( ! $cache instanceof Cache) { + throw new \InvalidArgumentException('No second-level cache is configured on the given EntityManager.'); + } + + if ( (! $ownerClass || ! $assoc) && ! $input->getOption('all')) { + throw new \InvalidArgumentException('Missing arguments "--owner-class" "--association"'); + } + + if ($input->getOption('flush')) { + $collectionRegion = $cache->getCollectionCacheRegion($ownerClass, $assoc); + + if ( ! $collectionRegion instanceof DefaultRegion) { + throw new \InvalidArgumentException(sprintf( + 'The option "--flush" expects a "Doctrine\ORM\Cache\Region\DefaultRegion", but got "%s".', + is_object($collectionRegion) ? get_class($collectionRegion) : gettype($collectionRegion) + )); + } + + $collectionRegion->getCache()->flushAll(); + + $output->writeln(sprintf('Flushing cache provider configured for "%s#%s"', $ownerClass, $assoc)); + + return; + } + + if ($input->getOption('all')) { + $output->writeln('Clearing all second-level cache collection regions'); + + $cache->evictEntityRegions(); + + return; + } + + if ($ownerId) { + $output->writeln(sprintf('Clearing second-level cache entry for collection "%s#%s" owner entity identified by "%s"', $ownerClass, $assoc, $ownerId)); + $cache->evictCollection($ownerClass, $assoc, $ownerId); + + return; + } + + $output->writeln(sprintf('Clearing second-level cache for collection "%s#%s"', $ownerClass, $assoc)); + $cache->evictCollectionRegion($ownerClass, $assoc); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php new file mode 100644 index 00000000000..21f6e9de9ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php @@ -0,0 +1,132 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache; + +/** + * Command to clear a entity cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class EntityRegionCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:region:entity') + ->setDescription('Clear a second-level cache entity region.') + ->addArgument('entity-class', InputArgument::OPTIONAL, 'The entity name.') + ->addArgument('entity-id', InputArgument::OPTIONAL, 'The entity identifier.') + ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all entity regions will be deleted/invalidated.') + ->addOption('flush', null, InputOption::VALUE_NONE,'If defined, all cache entries will be flushed.'); + + + $this->setHelp(<<%command.name% command is meant to clear a second-level cache entity region for an associated Entity Manager. +It is possible to delete/invalidate all entity region, a specific entity region or flushes the cache provider. + +The execution type differ on how you execute the command. +If you want to invalidate all entries for an entity region this command would do the work: + +%command.name% 'Entities\MyEntity' + +To invalidate a specific entry you should use : + +%command.name% 'Entities\MyEntity' 1 + +If you want to invalidate all entries for the all entity regions: + +%command.name% --all + +Alternatively, if you want to flush the configured cache provider for an entity region use this command: + +%command.name% 'Entities\MyEntity' --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 + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $entityClass = $input->getArgument('entity-class'); + $entityId = $input->getArgument('entity-id'); + $cache = $em->getCache(); + + if ( ! $cache instanceof Cache) { + throw new \InvalidArgumentException('No second-level cache is configured on the given EntityManager.'); + } + + if ( ! $entityClass && ! $input->getOption('all')) { + throw new \InvalidArgumentException('Invalid argument "--entity-class"'); + } + + if ($input->getOption('flush')) { + $entityRegion = $cache->getEntityCacheRegion($entityClass); + + if ( ! $entityRegion instanceof DefaultRegion) { + throw new \InvalidArgumentException(sprintf( + 'The option "--flush" expects a "Doctrine\ORM\Cache\Region\DefaultRegion", but got "%s".', + is_object($entityRegion) ? get_class($entityRegion) : gettype($entityRegion) + )); + } + + $entityRegion->getCache()->flushAll(); + + $output->writeln(sprintf('Flushing cache provider configured for entity named "%s"', $entityClass)); + + return; + } + + if ($input->getOption('all')) { + $output->writeln('Clearing all second-level cache entity regions'); + + $cache->evictEntityRegions(); + + return; + } + + if ($entityId) { + $output->writeln(sprintf('Clearing second-level cache entry for entity "%s" identified by "%s"', $entityClass, $entityId)); + $cache->evictEntity($entityClass, $entityId); + + return; + } + + $output->writeln(sprintf('Clearing second-level cache for entity "%s"', $entityClass)); + $cache->evictEntityRegion($entityClass); + } +} \ No newline at end of file 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 00000000000..e23d36b24c1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; +use Doctrine\Common\Cache\XcacheCache; + +/** + * Command to clear the metadata cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataCommand extends Command +{ + /** + * {@inheritdoc} + */ + 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.' + ) + )); + + $this->setHelp(<<%command.name% 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: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --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 + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, 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 ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + if ($cacheDriver instanceof XcacheCache) { + throw new \LogicException("Cannot clear XCache Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + + $output->writeln('Clearing ALL Metadata cache entries'); + + $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->writeln($message); + } +} 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 00000000000..f1be98d30fa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; +use Doctrine\Common\Cache\XcacheCache; + +/** + * Command to clear the query cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QueryCommand extends Command +{ + /** + * {@inheritdoc} + */ + 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.' + ) + )); + + $this->setHelp(<<%command.name% 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: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --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 + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, 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 ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + if ($cacheDriver instanceof XcacheCache) { + throw new \LogicException("Cannot clear XCache 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/QueryRegionCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php new file mode 100644 index 00000000000..b5d75d3b3c1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache; + +/** + * Command to clear a query cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class QueryRegionCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:region:query') + ->setDescription('Clear a second-level cache query region.') + ->addArgument('region-name', InputArgument::OPTIONAL, 'The query region to clear.') + ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all query regions will be deleted/invalidated.') + ->addOption('flush', null, InputOption::VALUE_NONE,'If defined, all cache entries will be flushed.'); + + + $this->setHelp(<<%command.name% command is meant to clear a second-level cache query region for an associated Entity Manager. +It is possible to delete/invalidate all query region, a specific query region or flushes the cache provider. + +The execution type differ on how you execute the command. +If you want to invalidate all entries for the default query region this command would do the work: + +%command.name% + +To invalidate entries for a specific query region you should use : + +%command.name% my_region_name + +If you want to invalidate all entries for the all query region: + +%command.name% --all + +Alternatively, if you want to flush the configured cache provider use this command: + +%command.name% my_region_name --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 + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $name = $input->getArgument('region-name'); + $cache = $em->getCache(); + + if ($name === null) { + $name = Cache::DEFAULT_QUERY_REGION_NAME; + } + + if ( ! $cache instanceof Cache) { + throw new \InvalidArgumentException('No second-level cache is configured on the given EntityManager.'); + } + + if ($input->getOption('flush')) { + $queryCache = $cache->getQueryCache($name); + $queryRegion = $queryCache->getRegion(); + + if ( ! $queryRegion instanceof DefaultRegion) { + throw new \InvalidArgumentException(sprintf( + 'The option "--flush" expects a "Doctrine\ORM\Cache\Region\DefaultRegion", but got "%s".', + is_object($queryRegion) ? get_class($queryRegion) : gettype($queryRegion) + )); + } + + $queryRegion->getCache()->flushAll(); + + $output->writeln(sprintf('Flushing cache provider configured for second-level cache query region named "%s"', $name)); + + return; + } + + if ($input->getOption('all')) { + $output->writeln('Clearing all second-level cache query regions'); + + $cache->evictQueryRegions(); + + return; + } + + $output->writeln(sprintf('Clearing second-level cache query region named "%s"', $name)); + $cache->evictQueryRegion($name); + } +} \ No newline at end of file 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 00000000000..c21f5545270 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; +use Doctrine\Common\Cache\XcacheCache; + +/** + * Command to clear the result cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ResultCommand extends Command +{ + /** + * {@inheritdoc} + */ + 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.' + ) + )); + + $this->setHelp(<<%command.name% 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: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --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 + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, 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 ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + if ($cacheDriver instanceof XcacheCache) { + throw new \LogicException("Cannot clear XCache Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->writeln('Clearing ALL Result cache entries'); + + $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->writeln($message); + } +} 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 00000000000..71d72deb84d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -0,0 +1,230 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\ConvertDoctrine1Schema; +use Doctrine\ORM\Tools\EntityGenerator; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to convert a Doctrine 1 schema to a Doctrine 2 mapping file. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1SchemaCommand extends Command +{ + /** + * @var EntityGenerator|null + */ + private $entityGenerator = null; + + /** + * @var ClassMetadataExporter|null + */ + private $metadataExporter = null; + + /** + * @return EntityGenerator + */ + public function getEntityGenerator() + { + if ($this->entityGenerator == null) { + $this->entityGenerator = new EntityGenerator(); + } + + return $this->entityGenerator; + } + + /** + * @param EntityGenerator $entityGenerator + * + * @return void + */ + 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 + * + * @return void + */ + public function setMetadataExporter(ClassMetadataExporter $metadataExporter) + { + $this->metadataExporter = $metadataExporter; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:convert-d1-schema') + ->setAliases(array('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(<<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($fromPaths, $destPath, $toType, $numSpaces, $extend, $output); + } + + /** + * @param array $fromPaths + * @param string $destPath + * @param string $toType + * @param int $numSpaces + * @param string|null $extend + * @param OutputInterface $output + * + * @throws \InvalidArgumentException + */ + public function convertDoctrine1Schema(array $fromPaths, $destPath, $toType, $numSpaces, $extend, OutputInterface $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) + ); + } + + 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) + ); + } + + 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->writeln(''); + + foreach ($metadata as $class) { + $output->writeln(sprintf('Processing entity "%s"', $class->name)); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->writeln(PHP_EOL . sprintf( + 'Converting Doctrine 1.X schema to "%s" mapping type in "%s"', $toType, $destPath + )); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} 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 00000000000..b229f4a6c08 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -0,0 +1,200 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Doctrine\ORM\Mapping\Driver\DatabaseDriver; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to convert your mapping information between the various formats. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertMappingCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:convert-mapping') + ->setAliases(array('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', 'f', 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, especially when using the --from-database +flag. + +Converting an existing database 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. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + if ($input->getOption('from-database') === true) { + $databaseDriver = new 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, 0775, true); + } + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } + + 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')); + + 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->writeln(sprintf('Processing entity "%s"', $class->name)); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->writeln(PHP_EOL . sprintf( + 'Exporting "%s" mapping information to "%s"', $toType, $destPath + )); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } + + /** + * @param string $toType + * @param string $destPath + * + * @return \Doctrine\ORM\Tools\Export\Driver\AbstractExporter + */ + 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 00000000000..3601570eb32 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Command to ensure that Doctrine is properly configured for a production environment. + * + * @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 Command +{ + /** + * {@inheritdoc} + */ + 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 existence.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + try { + $em->getConfiguration()->ensureProductionSettings(); + + if ($input->getOption('complete') !== null) { + $em->getConnection()->connect(); + } + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + + return 1; + } + + $output->writeln('Environment is correctly configured for production.'); + } +} 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 00000000000..bca32b7ec93 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to generate entity classes and method stubs from your mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateEntitiesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-entities') + ->setAliases(array('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_REQUIRED, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_REQUIRED, + 'Defines the number of indentation spaces', 4 + ), + new InputOption( + 'no-backup', null, InputOption::VALUE_NONE, + 'Flag to define if generator should avoid backuping existing entity file if it exists.' + ) + )) + ->setHelp(<<--update-entities or --regenerate-entities flags your existing +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 don't 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 + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, 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')) + ); + } + + 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')); + $entityGenerator->setBackupExisting(!$input->getOption('no-backup')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + + foreach ($metadatas as $metadata) { + $output->writeln( + sprintf('Processing entity "%s"', $metadata->name) + ); + } + + // Generating Entities + $entityGenerator->generate($metadatas, $destPath); + + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Entity classes generated to "%s"', $destPath)); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} 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 00000000000..21edb9dab83 --- /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; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to (re)generate the proxy classes used by doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateProxiesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-proxies') + ->setAliases(array('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, 0775, true); + } + + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not exist.", $em->getConfiguration()->getProxyDir()) + ); + } + + 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->writeln( + sprintf('Processing entity "%s"', $metadata->name) + ); + } + + // Generating Proxies + $em->getProxyFactory()->generateProxyClasses($metadatas, $destPath); + + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Proxy classes generated to "%s"', $destPath)); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} 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 00000000000..c6c13991427 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\EntityRepositoryGenerator; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to generate repository classes for mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateRepositoriesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-repositories') + ->setAliases(array('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')); + + $repositoryName = $em->getConfiguration()->getDefaultRepositoryClassName(); + + // 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')) + ); + } + + 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(); + + $generator->setDefaultRepositoryName($repositoryName); + + foreach ($metadatas as $metadata) { + if ($metadata->customRepositoryClassName) { + $output->writeln( + sprintf('Processing repository "%s"', $metadata->customRepositoryClassName) + ); + + $generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath); + + $numRepositories++; + } + } + + if ($numRepositories) { + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Repository classes generated to "%s"', $destPath) ); + } else { + $output->writeln('No Repository classes were found to be processed.' ); + } + } else { + $output->writeln('No Metadata Classes to process.' ); + } + } +} 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 00000000000..f23fe22c1bf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Doctrine\ORM\Mapping\MappingException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Show information about mapped entities. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + */ +class InfoCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:info') + ->setDescription('Show basic information about all mapped entities') + ->setHelp(<<%command.name% shows basic information about which +entities exist and possibly if their mapping information contains errors or +not. +EOT + ); + } + + /** + * {@inheritdoc} + */ + 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))); + + $failure = false; + + foreach ($entityClassNames as $entityClassName) { + try { + $entityManager->getClassMetadata($entityClassName); + $output->writeln(sprintf("[OK] %s", $entityClassName)); + } catch (MappingException $e) { + $output->writeln("[FAIL] ".$entityClassName); + $output->writeln(sprintf("%s", $e->getMessage())); + $output->writeln(''); + + $failure = true; + } + } + + return $failure ? 1 : 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php new file mode 100644 index 00000000000..ab7ea5577ea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php @@ -0,0 +1,298 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Doctrine\Common\Persistence\Mapping\MappingException; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Show information about mapped entities. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Daniel Leech + */ +final class MappingDescribeCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:mapping:describe') + ->addArgument('entityName', InputArgument::REQUIRED, 'Full or partial name of entity') + ->setDescription('Display information about mapped objects') + ->setHelp(<<%command.full_name% My\Namespace\Entity\MyEntity + +Or: + + %command.full_name% MyEntity +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $entityManager \Doctrine\ORM\EntityManagerInterface */ + $entityManager = $this->getHelper('em')->getEntityManager(); + + $this->displayEntity($input->getArgument('entityName'), $entityManager, $output); + + return 0; + } + + /** + * Display all the mapping information for a single Entity. + * + * @param string $entityName Full or partial entity class name + * @param EntityManagerInterface $entityManager + * @param OutputInterface $output + */ + private function displayEntity($entityName, EntityManagerInterface $entityManager, OutputInterface $output) + { + $table = new Table($output); + + $table->setHeaders(array('Field', 'Value')); + + $metadata = $this->getClassMetadata($entityName, $entityManager); + + array_map( + array($table, 'addRow'), + array_merge( + array( + $this->formatField('Name', $metadata->name), + $this->formatField('Root entity name', $metadata->rootEntityName), + $this->formatField('Custom generator definition', $metadata->customGeneratorDefinition), + $this->formatField('Custom repository class', $metadata->customRepositoryClassName), + $this->formatField('Mapped super class?', $metadata->isMappedSuperclass), + $this->formatField('Embedded class?', $metadata->isEmbeddedClass), + $this->formatField('Parent classes', $metadata->parentClasses), + $this->formatField('Sub classes', $metadata->subClasses), + $this->formatField('Embedded classes', $metadata->subClasses), + $this->formatField('Named queries', $metadata->namedQueries), + $this->formatField('Named native queries', $metadata->namedNativeQueries), + $this->formatField('SQL result set mappings', $metadata->sqlResultSetMappings), + $this->formatField('Identifier', $metadata->identifier), + $this->formatField('Inheritance type', $metadata->inheritanceType), + $this->formatField('Discriminator column', $metadata->discriminatorColumn), + $this->formatField('Discriminator value', $metadata->discriminatorValue), + $this->formatField('Discriminator map', $metadata->discriminatorMap), + $this->formatField('Generator type', $metadata->generatorType), + $this->formatField('Table', $metadata->table), + $this->formatField('Composite identifier?', $metadata->isIdentifierComposite), + $this->formatField('Foreign identifier?', $metadata->containsForeignIdentifier), + $this->formatField('Sequence generator definition', $metadata->sequenceGeneratorDefinition), + $this->formatField('Table generator definition', $metadata->tableGeneratorDefinition), + $this->formatField('Change tracking policy', $metadata->changeTrackingPolicy), + $this->formatField('Versioned?', $metadata->isVersioned), + $this->formatField('Version field', $metadata->versionField), + $this->formatField('Read only?', $metadata->isReadOnly), + + $this->formatEntityListeners($metadata->entityListeners), + ), + array($this->formatField('Association mappings:', '')), + $this->formatMappings($metadata->associationMappings), + array($this->formatField('Field mappings:', '')), + $this->formatMappings($metadata->fieldMappings) + ) + ); + + $table->render(); + } + + /** + * Return all mapped entity class names + * + * @param EntityManagerInterface $entityManager + * + * @return string[] + */ + private function getMappedEntities(EntityManagerInterface $entityManager) + { + $entityClassNames = $entityManager + ->getConfiguration() + ->getMetadataDriverImpl() + ->getAllClassNames(); + + if ( ! $entityClassNames) { + throw new \InvalidArgumentException( + '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.' + ); + } + + return $entityClassNames; + } + + /** + * Return the class metadata for the given entity + * name + * + * @param string $entityName Full or partial entity name + * @param EntityManagerInterface $entityManager + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function getClassMetadata($entityName, EntityManagerInterface $entityManager) + { + try { + return $entityManager->getClassMetadata($entityName); + } catch (MappingException $e) { + } + + $matches = array_filter( + $this->getMappedEntities($entityManager), + function ($mappedEntity) use ($entityName) { + return preg_match('{' . preg_quote($entityName) . '}', $mappedEntity); + } + ); + + if ( ! $matches) { + throw new \InvalidArgumentException(sprintf( + 'Could not find any mapped Entity classes matching "%s"', + $entityName + )); + } + + if (count($matches) > 1) { + throw new \InvalidArgumentException(sprintf( + 'Entity name "%s" is ambigous, possible matches: "%s"', + $entityName, implode(', ', $matches) + )); + } + + return $entityManager->getClassMetadata(current($matches)); + } + + /** + * Format the given value for console output + * + * @param mixed $value + * + * @return string + */ + private function formatValue($value) + { + if ('' === $value) { + return ''; + } + + if (null === $value) { + return 'Null'; + } + + if (is_bool($value)) { + return '' . ($value ? 'True' : 'False') . ''; + } + + if (empty($value)) { + return 'Empty'; + } + + if (is_array($value)) { + if (defined('JSON_UNESCAPED_UNICODE') && defined('JSON_UNESCAPED_SLASHES')) { + return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + + return json_encode($value); + } + + if (is_object($value)) { + return sprintf('<%s>', get_class($value)); + } + + if (is_scalar($value)) { + return $value; + } + + throw new \InvalidArgumentException(sprintf('Do not know how to format value "%s"', print_r($value, true))); + } + + /** + * Add the given label and value to the two column table output + * + * @param string $label Label for the value + * @param mixed $value A Value to show + * + * @return array + */ + private function formatField($label, $value) + { + if (null === $value) { + $value = 'None'; + } + + return array(sprintf('%s', $label), $this->formatValue($value)); + } + + /** + * Format the association mappings + * + * @param array + * + * @return array + */ + private function formatMappings(array $propertyMappings) + { + $output = array(); + + foreach ($propertyMappings as $propertyName => $mapping) { + $output[] = $this->formatField(sprintf(' %s', $propertyName), ''); + + foreach ($mapping as $field => $value) { + $output[] = $this->formatField(sprintf(' %s', $field), $this->formatValue($value)); + } + } + + return $output; + } + + /** + * Format the entity listeners + * + * @param array $entityListeners + * + * @return array + */ + private function formatEntityListeners(array $entityListeners) + { + return $this->formatField( + 'Entity listeners', + array_map( + function ($entityListener) { + return get_class($entityListener); + }, + $entityListeners + ) + ); + } +} 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 00000000000..abd999aef09 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php @@ -0,0 +1,133 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\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\Common\Util\Debug; + +/** + * Command to execute DQL queries in a given EntityManager. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunDqlCommand extends Command +{ + /** + * {@inheritdoc} + */ + 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 + ), + new InputOption( + 'show-sql', null, InputOption::VALUE_NONE, + 'Dump generated SQL instead of executing query' + ) + )) + ->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); + } + + if ($input->getOption('show-sql')) { + $output->writeln(Debug::dump($query->getSQL(), 2, true, false)); + return; + } + + $resultSet = $query->execute(array(), constant($hydrationMode)); + + $output->writeln(Debug::dump($resultSet, $input->getOption('depth'), true, false)); + } +} 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 00000000000..0aabd630c51 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Base class for CreateCommand, DropCommand and UpdateCommand. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractCommand extends Command +{ + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param SchemaTool $schemaTool + * @param array $metadatas + * + * @return null|int Null or 0 if everything went fine, or an error code. + */ + abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas); + + /** + * {@inheritdoc} + */ + 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 SchemaTool($em); + + return $this->executeSchemaCommand($input, $output, $tool, $metadatas); + } else { + $output->writeln('No Metadata Classes to process.'); + return 0; + } + } +} 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 00000000000..b2af0ca2cfd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -0,0 +1,85 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to create the database schema for a set of classes based on their mappings. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CreateCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + 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 trying to apply generated SQLs into EntityManager Storage Connection, output them.' + ) + )) + ->setHelp(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + if ($input->getOption('dump-sql')) { + $sqls = $schemaTool->getCreateSchemaSql($metadatas); + $output->writeln(implode(';' . PHP_EOL, $sqls) . ';'); + } else { + $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + + $output->writeln('Creating database schema...'); + $schemaTool->createSchema($metadatas); + $output->writeln('Database schema created successfully!'); + } + + return 0; + } +} 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 00000000000..b22237831ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to drop the database schema for a set of classes based on their mappings. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DropCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + 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 trying to apply generated SQLs into EntityManager Storage Connection, output them.' + ), + new InputOption( + 'force', 'f', 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(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + $isFullDatabaseDrop = $input->getOption('full-database'); + + if ($input->getOption('dump-sql')) { + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + $output->writeln(implode(';' . PHP_EOL, $sqls)); + + return 0; + } + + if ($input->getOption('force')) { + $output->writeln('Dropping database schema...'); + + if ($isFullDatabaseDrop) { + $schemaTool->dropDatabase(); + } else { + $schemaTool->dropSchema($metadatas); + } + + $output->writeln('Database schema dropped successfully!'); + + return 0; + } + + $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + + if (count($sqls)) { + $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 - or both - 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())); + + return 1; + } + + $output->writeln('Nothing to drop. The database is empty!'); + + return 0; + } +} 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 00000000000..a0e942a344d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -0,0 +1,157 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to generate the SQL needed to update the database schema to match + * the current mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Ryan Weaver + */ +class UpdateCommand extends AbstractCommand +{ + /** + * @var string + */ + protected $name = 'orm:schema-tool:update'; + + /** + * {@inheritdoc} + */ + 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', 'f', InputOption::VALUE_NONE, + 'Causes the generated SQL statements to be physically executed against your database.' + ), + )); + + $this->setHelp(<<%command.name% 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: + +%command.name% --dump-sql + +Alternatively, you can execute the generated queries: + +%command.name% --force + +If both options are specified, the queries are output and then executed: + +%command.name% --dump-sql --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. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + 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'); + + $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 0; + } + + $dumpSql = true === $input->getOption('dump-sql'); + $force = true === $input->getOption('force'); + + if ($dumpSql) { + $output->writeln(implode(';' . PHP_EOL, $sqls) . ';'); + } + + if ($force) { + if ($dumpSql) { + $output->writeln(''); + } + $output->writeln('Updating database schema...'); + $schemaTool->updateSchema($metadatas, $saveMode); + + $pluralization = (1 === count($sqls)) ? 'query was' : 'queries were'; + + $output->writeln(sprintf('Database schema updated successfully! "%s" %s executed', count($sqls), $pluralization)); + } + + if ($dumpSql || $force) { + return 0; + } + + $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 - or both - 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())); + + return 1; + } +} 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 00000000000..eb7697ccb73 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaValidator; + +/** + * Command to validate that the current mapping is valid. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ValidateSchemaCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:validate-schema') + ->setDescription('Validate the mapping files.') + ->addOption( + 'skip-mapping', + null, + InputOption::VALUE_NONE, + 'Skip the mapping validation check' + ) + ->addOption( + 'skip-sync', + null, + InputOption::VALUE_NONE, + 'Skip checking if the mapping is in sync with the database' + ) + ->setHelp( + <<getHelper('em')->getEntityManager(); + $validator = new SchemaValidator($em); + $exit = 0; + + if ($input->getOption('skip-mapping')) { + $output->writeln('[Mapping] Skipped mapping check.'); + } elseif ($errors = $validator->validateMapping()) { + foreach ($errors as $className => $errorMessages) { + $output->writeln("[Mapping] FAIL - The entity-class '" . $className . "' mapping is invalid:"); + + foreach ($errorMessages as $errorMessage) { + $output->writeln('* ' . $errorMessage); + } + + $output->writeln(''); + } + + $exit += 1; + } else { + $output->writeln('[Mapping] OK - The mapping files are correct.'); + } + + if ($input->getOption('skip-sync')) { + $output->writeln('[Database] SKIPPED - The database was not checked for synchronicity.'); + } elseif (!$validator->schemaInSyncWithMetadata()) { + $output->writeln('[Database] FAIL - The database schema is not in sync with the current mapping file.'); + $exit += 2; + } else { + $output->writeln('[Database] OK - The database schema is in sync with the mapping files.'); + } + + 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 00000000000..55ec3791c14 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\ORM\Version; +use Doctrine\ORM\EntityManagerInterface; + +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; + +/** + * Handles running the Console Tools inside Symfony Console context. + */ +class ConsoleRunner +{ + /** + * Create a Symfony Console HelperSet + * + * @param EntityManagerInterface $entityManager + * @return HelperSet + */ + public static function createHelperSet(EntityManagerInterface $entityManager) + { + return new HelperSet(array( + 'db' => new ConnectionHelper($entityManager->getConnection()), + 'em' => new EntityManagerHelper($entityManager) + )); + } + + /** + * Runs console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param \Symfony\Component\Console\Command\Command[] $commands + * + * @return void + */ + static public function run(HelperSet $helperSet, $commands = array()) + { + $cli = self::createApplication($helperSet, $commands); + $cli->run(); + } + + /** + * Creates a console application with the given helperset and + * optional commands. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param array $commands + * + * @return \Symfony\Component\Console\Application + */ + static public function createApplication(HelperSet $helperSet, $commands = array()) + { + $cli = new Application('Doctrine Command Line Interface', Version::VERSION); + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + self::addCommands($cli); + $cli->addCommands($commands); + + return $cli; + } + + /** + * @param Application $cli + * + * @return void + */ + 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(), + new \Doctrine\ORM\Tools\Console\Command\MappingDescribeCommand(), + )); + } + + static public function printCliConfigTemplate() + { + echo <<<'HELP' +You are missing a "cli-config.php" or "config/cli-config.php" file in your +project, which is required to get the Doctrine Console working. You can use the +following sample as a template: + +. + */ + +namespace Doctrine\ORM\Tools\Console\Helper; + +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Helper\Helper; + + +/** + * Doctrine CLI Connection Helper. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityManagerHelper extends Helper +{ + /** + * Doctrine ORM EntityManagerInterface. + * + * @var EntityManagerInterface + */ + protected $_em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->_em = $em; + } + + /** + * Retrieves Doctrine ORM EntityManager. + * + * @return EntityManagerInterface + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * {@inheritdoc} + */ + 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 00000000000..5a72b7d6d00 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +/** + * Used by CLI Tools to restrict entity-based commands to given patterns. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataFilter extends \FilterIterator implements \Countable +{ + /** + * @var array + */ + private $filter = array(); + + /** + * 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); + } + + /** + * @param \ArrayIterator $metadata + * @param array|string $filter + */ + public function __construct(\ArrayIterator $metadata, $filter) + { + $this->filter = (array) $filter; + + parent::__construct($metadata); + } + + /** + * @return bool + */ + public function accept() + { + if (count($this->filter) == 0) { + return true; + } + + $it = $this->getInnerIterator(); + $metadata = $it->current(); + + foreach ($this->filter as $filter) { + $pregResult = preg_match("/$filter/", $metadata->name); + + if ($pregResult === false) { + throw new \RuntimeException( + sprintf("Error while evaluating regex '/%s/'.", $filter) + ); + } + + if ($pregResult === 0) { + return false; + } + + if ($pregResult) { + return true; + } + } + + return false; + } + + /** + * @return int + */ + public function count() + { + return count($this->getInnerIterator()); + } +} 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 00000000000..52f3c92b972 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php @@ -0,0 +1,344 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Types\Type; +use Symfony\Component\Yaml\Yaml; + +/** + * Class to help with converting Doctrine 1 schema files to Doctrine 2 mapping files + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1Schema +{ + /** + * @var array + */ + private $from; + + /** + * @var array + */ + 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; + } + + /** + * Gets an array of ClassMetadataInfo instances from the passed + * Doctrine 1 schema. + * + * @return array 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) Yaml::parse(file_get_contents($file))); + } + } else { + $schema = array_merge($schema, (array) Yaml::parse(file_get_contents($path))); + } + } + + $metadatas = array(); + foreach ($schema as $className => $mappingInformation) { + $metadatas[] = $this->convertToClassMetadataInfo($className, $mappingInformation); + } + + return $metadatas; + } + + /** + * @param string $className + * @param array $mappingInformation + * + * @return \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + 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; + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + 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]; + } + } + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + 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); + } + } + + /** + * @param string $className + * @param string $name + * @param string|array $column + * @param ClassMetadataInfo $metadata + * + * @return array + * + * @throws ToolsException + */ + 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 ( ! 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); + } elseif (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; + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertIndexes($className, array $model, ClassMetadataInfo $metadata) + { + if (empty($model['indexes'])) { + return; + } + + foreach ($model['indexes'] as $name => $index) { + $type = (isset($index['type']) && $index['type'] == 'unique') + ? 'uniqueConstraints' : 'indexes'; + + $metadata->table[$type][$name] = array( + 'columns' => $index['fields'] + ); + } + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertRelations($className, array $model, ClassMetadataInfo $metadata) + { + if (empty($model['relations'])) { + return; + } + + 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'; + } elseif ($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 00000000000..5196dc3391c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php @@ -0,0 +1,184 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\Persistence\Proxy; +use Doctrine\ORM\EntityManagerInterface; +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 +{ + /** + * @var string + */ + private $file; + + /** + * @var string + */ + private $context; + + /** + * Pass a stream and context 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; + } + + /** + * @param \Doctrine\ORM\Event\OnFlushEventArgs $args + * + * @return void + */ + public function onFlush(OnFlushEventArgs $args) + { + $this->dumpIdentityMap($args->getEntityManager()); + } + + /** + * Dumps the contents of the identity map into a stream. + * + * @param EntityManagerInterface $em + * + * @return void + */ + public function dumpIdentityMap(EntityManagerInterface $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 $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->getFieldValue($entity, $field); + + 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"); + } elseif ($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); + } + + /** + * @param mixed $var + * + * @return string + */ + private function getType($var) + { + if (is_object($var)) { + $refl = new \ReflectionObject($var); + + return $refl->getShortname(); + } + + return gettype($var); + } + + /** + * @param object $entity + * @param UnitOfWork $uow + * + * @return string + */ + private function getIdString($entity, UnitOfWork $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]"; + } elseif ($state == UnitOfWork::STATE_REMOVED) { + $idstring .= " [REMOVED]"; + } elseif ($state == UnitOfWork::STATE_MANAGED) { + $idstring .= " [MANAGED]"; + } elseif ($state == UnitOfwork::STATE_DETACHED) { + $idstring .= " [DETACHED]"; + } + + return $idstring; + } +} 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 00000000000..7a3ec6f893c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\Persistence\Mapping\StaticReflectionService; +use Doctrine\ORM\Mapping\ClassMetadataFactory; + +/** + * 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. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DisconnectedClassMetadataFactory extends ClassMetadataFactory +{ + /** + * @return \Doctrine\Common\Persistence\Mapping\StaticReflectionService + */ + public function getReflectionService() + { + return new 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 00000000000..9027d9aa5b7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -0,0 +1,1819 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Types\Type; + +/** + * 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'); + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityGenerator +{ + /** + * Specifies class fields should be protected. + */ + const FIELD_VISIBLE_PROTECTED = 'protected'; + + /** + * Specifies class fields should be private. + */ + const FIELD_VISIBLE_PRIVATE = 'private'; + + /** + * @var bool + */ + protected $backupExisting = true; + + /** + * The extension to use for written php files. + * + * @var string + */ + protected $extension = '.php'; + + /** + * Whether or not the current ClassMetadataInfo instance is new or old. + * + * @var boolean + */ + protected $isNew = true; + + /** + * @var array + */ + protected $staticReflection = array(); + + /** + * Number of spaces to use for indention in generated code. + */ + protected $numSpaces = 4; + + /** + * The actual spaces to use for indention. + * + * @var string + */ + protected $spaces = ' '; + + /** + * The class all generated entities should extend. + * + * @var string + */ + protected $classToExtend; + + /** + * Whether or not to generation annotations. + * + * @var boolean + */ + protected $generateAnnotations = false; + + /** + * @var string + */ + protected $annotationsPrefix = ''; + + /** + * Whether or not to generate sub methods. + * + * @var boolean + */ + protected $generateEntityStubMethods = false; + + /** + * Whether or not to update the entity class if it exists already. + * + * @var boolean + */ + protected $updateEntityIfExists = false; + + /** + * Whether or not to re-generate entity class if it exists already. + * + * @var boolean + */ + protected $regenerateEntityIfExists = false; + + /** + * Visibility of the field + * + * @var string + */ + protected $fieldVisibility = 'private'; + + /** + * Whether or not to make generated embeddables immutable. + * + * @var boolean. + */ + protected $embeddablesImmutable = false; + + /** + * Hash-map for handle types. + * + * @var array + */ + protected $typeAlias = array( + Type::DATETIMETZ => '\DateTime', + Type::DATETIME => '\DateTime', + Type::DATE => '\DateTime', + Type::TIME => '\DateTime', + Type::OBJECT => '\stdClass', + Type::BIGINT => 'integer', + Type::SMALLINT => 'integer', + Type::TEXT => 'string', + Type::BLOB => 'string', + Type::DECIMAL => 'string', + Type::JSON_ARRAY => 'array', + Type::SIMPLE_ARRAY => 'array', + ); + + /** + * Hash-map to handle generator types string. + * + * @var array + */ + protected static $generatorStrategyMap = array( + ClassMetadataInfo::GENERATOR_TYPE_AUTO => 'AUTO', + ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE => 'SEQUENCE', + ClassMetadataInfo::GENERATOR_TYPE_TABLE => 'TABLE', + ClassMetadataInfo::GENERATOR_TYPE_IDENTITY => 'IDENTITY', + ClassMetadataInfo::GENERATOR_TYPE_NONE => 'NONE', + ClassMetadataInfo::GENERATOR_TYPE_UUID => 'UUID', + ClassMetadataInfo::GENERATOR_TYPE_CUSTOM => 'CUSTOM' + ); + + /** + * Hash-map to handle the change tracking policy string. + * + * @var array + */ + protected static $changeTrackingPolicyMap = array( + ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT => 'DEFERRED_IMPLICIT', + ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT => 'DEFERRED_EXPLICIT', + ClassMetadataInfo::CHANGETRACKING_NOTIFY => 'NOTIFY', + ); + + /** + * Hash-map to handle the inheritance type string. + * + * @var array + */ + protected static $inheritanceTypeMap = array( + ClassMetadataInfo::INHERITANCE_TYPE_NONE => 'NONE', + ClassMetadataInfo::INHERITANCE_TYPE_JOINED => 'JOINED', + ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE => 'SINGLE_TABLE', + ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS => 'TABLE_PER_CLASS', + ); + + /** + * @var string + */ + protected static $classTemplate = +' + + + +{ + +} +'; + + /** + * @var string + */ + protected static $getMethodTemplate = +'/** + * + * + * @return + */ +public function () +{ +return $this->; +}'; + + /** + * @var string + */ + protected static $setMethodTemplate = +'/** + * + * + * @param $ + * + * @return + */ +public function ($) +{ +$this-> = $; + +return $this; +}'; + + /** + * @var string + */ + protected static $addMethodTemplate = +'/** + * + * + * @param $ + * + * @return + */ +public function ($) +{ +$this->[] = $; + +return $this; +}'; + + /** + * @var string + */ + protected static $removeMethodTemplate = +'/** + * + * + * @param $ + */ +public function ($) +{ +$this->->removeElement($); +}'; + + /** + * @var string + */ + protected static $lifecycleCallbackMethodTemplate = +'/** + * @ + */ +public function () +{ +// Add your code here +}'; + + /** + * @var string + */ + protected static $constructorMethodTemplate = +'/** + * Constructor + */ +public function __construct() +{ + +} +'; + + /** + * @var string + */ + protected static $embeddableConstructorMethodTemplate = +'/** + * Constructor + * + * + */ +public function __construct() +{ + +} +'; + + /** + * Constructor. + */ + public function __construct() + { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + $this->annotationsPrefix = 'ORM\\'; + } + } + + /** + * Generates and writes 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); + } + } + + /** + * Generates and writes entity class to disk for the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * @param string $outputDirectory + * + * @return void + * + * @throws \RuntimeException + */ + 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, 0775, 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 + } elseif ( ! $this->isNew && $this->updateEntityIfExists) { + file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path)); + } + chmod($path, 0664); + } + + /** + * Generates a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + public function generateEntityClass(ClassMetadataInfo $metadata) + { + $placeHolders = array( + '', + '', + '', + '', + '' + ); + + $replacements = array( + $this->generateEntityNamespace($metadata), + $this->generateEntityUse(), + $this->generateEntityDocBlock($metadata), + $this->generateEntityClassName($metadata), + $this->generateEntityBody($metadata) + ); + + $code = str_replace($placeHolders, $replacements, static::$classTemplate) . "\n"; + + return str_replace('', $this->spaces, $code); + } + + /** + * Generates the updated code for the given ClassMetadataInfo and entity at path. + * + * @param ClassMetadataInfo $metadata + * @param string $path + * + * @return string + */ + 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" : '') . "}\n"; + } + + /** + * Sets 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; + } + + /** + * Sets the extension to use when writing php files to disk. + * + * @param string $extension + * + * @return void + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * Sets the name of the class the generated classes should extend from. + * + * @param string $classToExtend + * + * @return void + */ + public function setClassToExtend($classToExtend) + { + $this->classToExtend = $classToExtend; + } + + /** + * Sets whether or not to generate annotations for the entity. + * + * @param bool $bool + * + * @return void + */ + public function setGenerateAnnotations($bool) + { + $this->generateAnnotations = $bool; + } + + /** + * Sets the class fields visibility for the entity (can either be private or protected). + * + * @param bool $visibility + * + * @return void + * + * @throws \InvalidArgumentException + */ + public function setFieldVisibility($visibility) + { + if ($visibility !== static::FIELD_VISIBLE_PRIVATE && $visibility !== static::FIELD_VISIBLE_PROTECTED) { + throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility); + } + + $this->fieldVisibility = $visibility; + } + + /** + * Sets whether or not to generate immutable embeddables. + * + * @param boolean $embeddablesImmutable + */ + public function setEmbeddablesImmutable($embeddablesImmutable) + { + $this->embeddablesImmutable = (boolean) $embeddablesImmutable; + } + + /** + * Sets an annotation prefix. + * + * @param string $prefix + * + * @return void + */ + public function setAnnotationPrefix($prefix) + { + $this->annotationsPrefix = $prefix; + } + + /** + * Sets 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; + } + + /** + * Sets whether or not to regenerate the entity if it exists. + * + * @param bool $bool + * + * @return void + */ + public function setRegenerateEntityIfExists($bool) + { + $this->regenerateEntityIfExists = $bool; + } + + /** + * Sets 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? + * + * @param bool $bool + * + * @return void + */ + public function setBackupExisting($bool) + { + $this->backupExisting = $bool; + } + + /** + * @param string $type + * + * @return string + */ + protected function getType($type) + { + if (isset($this->typeAlias[$type])) { + return $this->typeAlias[$type]; + } + + return $type; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityNamespace(ClassMetadataInfo $metadata) + { + if ($this->hasNamespace($metadata)) { + return 'namespace ' . $this->getNamespace($metadata) .';'; + } + } + + protected function generateEntityUse() + { + if ($this->generateAnnotations) { + return "\n".'use Doctrine\ORM\Mapping as ORM;'."\n"; + } else { + return ""; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityClassName(ClassMetadataInfo $metadata) + { + return 'class ' . $this->getClassName($metadata) . + ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityBody(ClassMetadataInfo $metadata) + { + $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata); + $embeddedProperties = $this->generateEntityEmbeddedProperties($metadata); + $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata); + $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null; + $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata); + + $code = array(); + + if ($fieldMappingProperties) { + $code[] = $fieldMappingProperties; + } + + if ($embeddedProperties) { + $code[] = $embeddedProperties; + } + + if ($associationMappingProperties) { + $code[] = $associationMappingProperties; + } + + $code[] = $this->generateEntityConstructor($metadata); + + if ($stubMethods) { + $code[] = $stubMethods; + } + + if ($lifecycleCallbackMethods) { + $code[] = $lifecycleCallbackMethods; + } + + return implode("\n", $code); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityConstructor(ClassMetadataInfo $metadata) + { + if ($this->hasMethod('__construct', $metadata)) { + return ''; + } + + if ($metadata->isEmbeddedClass && $this->embeddablesImmutable) { + return $this->generateEmbeddableConstructor($metadata); + } + + $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), static::$constructorMethodTemplate)); + } + + return ''; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + private function generateEmbeddableConstructor(ClassMetadataInfo $metadata) + { + $paramTypes = array(); + $paramVariables = array(); + $params = array(); + $fields = array(); + + // Resort fields to put optional fields at the end of the method signature. + $requiredFields = array(); + $optionalFields = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if (empty($fieldMapping['nullable'])) { + $requiredFields[] = $fieldMapping; + + continue; + } + + $optionalFields[] = $fieldMapping; + } + + $fieldMappings = array_merge($requiredFields, $optionalFields); + + foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { + $paramType = '\\' . ltrim($embeddedClass['class'], '\\'); + $paramVariable = '$' . $fieldName; + + $paramTypes[] = $paramType; + $paramVariables[] = $paramVariable; + $params[] = $paramType . ' ' . $paramVariable; + $fields[] = '$this->' . $fieldName . ' = ' . $paramVariable . ';'; + } + + foreach ($fieldMappings as $fieldMapping) { + if (isset($fieldMapping['declaredField']) && + isset($metadata->embeddedClasses[$fieldMapping['declaredField']]) + ) { + continue; + } + + $paramTypes[] = $this->getType($fieldMapping['type']) . (!empty($fieldMapping['nullable']) ? '|null' : ''); + $param = '$' . $fieldMapping['fieldName']; + $paramVariables[] = $param; + + if ($fieldMapping['type'] === 'datetime') { + $param = $this->getType($fieldMapping['type']) . ' ' . $param; + } + + if (!empty($fieldMapping['nullable'])) { + $param .= ' = null'; + } + + $params[] = $param; + + $fields[] = '$this->' . $fieldMapping['fieldName'] . ' = $' . $fieldMapping['fieldName'] . ';'; + } + + $maxParamTypeLength = max(array_map('strlen', $paramTypes)); + $paramTags = array_map( + function ($type, $variable) use ($maxParamTypeLength) { + return '@param ' . $type . str_repeat(' ', $maxParamTypeLength - strlen($type) + 1) . $variable; + }, + $paramTypes, + $paramVariables + ); + + // Generate multi line constructor if the signature exceeds 120 characters. + if (array_sum(array_map('strlen', $params)) + count($params) * 2 + 29 > 120) { + $delimiter = "\n" . $this->spaces; + $params = $delimiter . implode(',' . $delimiter, $params) . "\n"; + } else { + $params = implode(', ', $params); + } + + $replacements = array( + '' => implode("\n * ", $paramTags), + '' => $params, + '' => implode("\n" . $this->spaces, $fields), + ); + + $constructor = str_replace( + array_keys($replacements), + array_values($replacements), + static::$embeddableConstructorMethodTemplate + ); + + return $this->prefixCodeWithSpaces($constructor); + } + + /** + * @todo this won't work if there is a namespace in brackets and a class outside of it. + * + * @param string $src + * + * @return void + */ + protected 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]; + } elseif (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; + } elseif ($token[0] == T_CLASS && $tokens[$i-1][0] != T_DOUBLE_COLON) { + $inClass = true; + } elseif ($token[0] == T_FUNCTION) { + if ($tokens[$i+2][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+2][1]); + } elseif ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+3][1]); + } + } elseif (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); + } + } + } + + /** + * @param string $property + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasProperty($property, ClassMetadataInfo $metadata) + { + if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) { + // don't generate property if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name); + if ($reflClass->hasProperty($property)) { + return true; + } + } + + // check traits for existing property + foreach ($this->getTraits($metadata) as $trait) { + if ($trait->hasProperty($property)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($property, $this->staticReflection[$metadata->name]['properties']) + ); + } + + /** + * @param string $method + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasMethod($method, ClassMetadataInfo $metadata) + { + if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) { + // don't generate method if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name); + + if ($reflClass->hasMethod($method)) { + return true; + } + } + + // check traits for existing method + foreach ($this->getTraits($metadata) as $trait) { + if ($trait->hasMethod($method)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array(strtolower($method), $this->staticReflection[$metadata->name]['methods']) + ); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return array + */ + protected function getTraits(ClassMetadataInfo $metadata) + { + if (! ($metadata->reflClass !== null || class_exists($metadata->name))) { + return []; + } + + $reflClass = $metadata->reflClass === null + ? new \ReflectionClass($metadata->name) + : $metadata->reflClass; + + $traits = array(); + + while ($reflClass !== false) { + $traits = array_merge($traits, $reflClass->getTraits()); + + $reflClass = $reflClass->getParentClass(); + } + + return $traits; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasNamespace(ClassMetadataInfo $metadata) + { + return strpos($metadata->name, '\\') ? true : false; + } + + /** + * @return bool + */ + protected function extendsClass() + { + return $this->classToExtend ? true : false; + } + + /** + * @return string + */ + protected function getClassToExtend() + { + return $this->classToExtend; + } + + /** + * @return string + */ + protected function getClassToExtendName() + { + $refl = new \ReflectionClass($this->getClassToExtend()); + + return '\\' . $refl->getName(); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function getClassName(ClassMetadataInfo $metadata) + { + return ($pos = strrpos($metadata->name, '\\')) + ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function getNamespace(ClassMetadataInfo $metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityDocBlock(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = '/**'; + $lines[] = ' * ' . $this->getClassName($metadata); + + if ($this->generateAnnotations) { + $lines[] = ' *'; + + $methods = array( + 'generateTableAnnotation', + 'generateInheritanceAnnotation', + 'generateDiscriminatorColumnAnnotation', + 'generateDiscriminatorMapAnnotation', + 'generateEntityAnnotation', + ); + + foreach ($methods as $method) { + if ($code = $this->$method($metadata)) { + $lines[] = ' * ' . $code; + } + } + + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks'; + } + } + + $lines[] = ' */'; + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityAnnotation(ClassMetadataInfo $metadata) + { + $prefix = '@' . $this->annotationsPrefix; + + if ($metadata->isEmbeddedClass) { + return $prefix . 'Embeddable'; + } + + $customRepository = $metadata->customRepositoryClassName + ? '(repositoryClass="' . $metadata->customRepositoryClassName . '")' + : ''; + + return $prefix . ($metadata->isMappedSuperclass ? 'MappedSuperclass' : 'Entity') . $customRepository; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateTableAnnotation($metadata) + { + if ($metadata->isEmbeddedClass) { + return ''; + } + + $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['options']) && $metadata->table['options']) { + $table[] = 'options={' . $this->exportTableOptions((array) $metadata->table['options']) . '}'; + } + + 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) . ')'; + } + + /** + * @param string $constraintName + * @param array $constraints + * + * @return string + */ + protected 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); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateInheritanceAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateDiscriminatorColumnAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $discrColumn = $metadata->discriminatorColumn; + $columnDefinition = 'name="' . $discrColumn['name'] + . '", type="' . $discrColumn['type'] + . '", length=' . $discrColumn['length']; + + return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected 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) . '})'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityStubMethods(ClassMetadataInfo $metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if (isset($fieldMapping['declaredField']) && + isset($metadata->embeddedClasses[$fieldMapping['declaredField']]) + ) { + continue; + } + + if (( ! isset($fieldMapping['id']) || + ! $fieldMapping['id'] || + $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE + ) && (! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) + ) { + 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->embeddedClasses as $fieldName => $embeddedClass) { + if (isset($embeddedClass['declaredField'])) { + continue; + } + + if ( ! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) { + if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldName, $embeddedClass['class'])) { + $methods[] = $code; + } + } + + if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldName, $embeddedClass['class'])) { + $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; + } + } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + if ($code = $this->generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + if ($code = $this->generateEntityStubMethod($metadata, 'remove', $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); + } + + /** + * @param array $associationMapping + * + * @return bool + */ + protected function isAssociationIsNullable($associationMapping) + { + if (isset($associationMapping['id']) && $associationMapping['id']) { + return false; + } + + if (isset($associationMapping['joinColumns'])) { + $joinColumns = $associationMapping['joinColumns']; + } else { + //@todo there is no way to retrieve targetEntity metadata + $joinColumns = array(); + } + + foreach ($joinColumns as $joinColumn) { + if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + return false; + } + } + + return true; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected 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 ""; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected 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 . $this->fieldVisibility . ' $' . $associationMapping['fieldName'] + . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName']) || + ( + isset($fieldMapping['declaredField']) && + isset($metadata->embeddedClasses[$fieldMapping['declaredField']]) + ) + ) { + continue; + } + + $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName'] + . (isset($fieldMapping['options']['default']) ? ' = ' . var_export($fieldMapping['options']['default'], true) : null) . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityEmbeddedProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { + if (isset($embeddedClass['declaredField']) || $this->hasProperty($fieldName, $metadata)) { + continue; + } + + $lines[] = $this->generateEmbeddedPropertyDocBlock($embeddedClass); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldName . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * @param string $type + * @param string $fieldName + * @param string|null $typeHint + * @param string|null $defaultValue + * + * @return string + */ + protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null) + { + $methodName = $type . Inflector::classify($fieldName); + $variableName = Inflector::camelize($fieldName); + if (in_array($type, array("add", "remove"))) { + $methodName = Inflector::singularize($methodName); + $variableName = Inflector::singularize($variableName); + } + + if ($this->hasMethod($methodName, $metadata)) { + return ''; + } + $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName); + + $var = sprintf('%sMethodTemplate', $type); + $template = static::$$var; + + $methodTypeHint = null; + $types = Type::getTypesMap(); + $variableType = $typeHint ? $this->getType($typeHint) : null; + + if ($typeHint && ! isset($types[$typeHint])) { + $variableType = '\\' . ltrim($variableType, '\\'); + $methodTypeHint = '\\' . $typeHint . ' '; + } + + $replacements = array( + '' => ucfirst($type) . ' ' . $variableName, + '' => $methodTypeHint, + '' => $variableType, + '' => $variableName, + '' => $methodName, + '' => $fieldName, + '' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '', + '' => $this->getClassName($metadata) + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + $template + ); + + return $this->prefixCodeWithSpaces($method); + } + + /** + * @param string $name + * @param string $methodName + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected 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), + static::$lifecycleCallbackMethodTemplate + ); + + return $this->prefixCodeWithSpaces($method); + } + + /** + * @param array $joinColumn + * + * @return string + */ + protected 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) . ')'; + } + + /** + * @param array $associationMapping + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + + if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection'; + } else { + $lines[] = $this->spaces . ' * @var \\' . ltrim($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"'; + + if (count($cascades) === 5) { + $cascades = array('"all"'); + } + + $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) { + $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false'); + } + + if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) { + $fetchMap = array( + ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY', + ClassMetadataInfo::FETCH_EAGER => 'EAGER', + ); + + $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"'; + } + + $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={'; + + $joinColumnsLines = array(); + + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $joinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = implode(",". PHP_EOL, $joinColumnsLines); + $lines[] = $this->spaces . ' * },'; + $lines[] = $this->spaces . ' * inverseJoinColumns={'; + + $inverseJoinColumnsLines = array(); + + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $inverseJoinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines); + $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); + } + + /** + * @param array $fieldMapping + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + $lines[] = $this->spaces . ' * @var ' . $this->getType($fieldMapping['type']); + + 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['unsigned']) && $fieldMapping['unsigned']) { + $column[] = 'options={"unsigned"=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); + } + + /** + * @param array $embeddedClass + * + * @return string + */ + protected function generateEmbeddedPropertyDocBlock(array $embeddedClass) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + $lines[] = $this->spaces . ' * @var \\' . ltrim($embeddedClass['class'], '\\'); + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + $embedded = array('class="' . $embeddedClass['class'] . '"'); + + if (isset($fieldMapping['columnPrefix'])) { + $embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true); + } + + $lines[] = $this->spaces . ' * @' . + $this->annotationsPrefix . 'Embedded(' . implode(', ', $embedded) . ')'; + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + /** + * @param string $code + * @param int $num + * + * @return string + */ + protected function prefixCodeWithSpaces($code, $num = 1) + { + $lines = explode("\n", $code); + + foreach ($lines as $key => $value) { + if ( ! empty($value)) { + $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key]; + } + } + + return implode("\n", $lines); + } + + /** + * @param integer $type The inheritance type used by the class and its subclasses. + * + * @return string The literal string for the inheritance type. + * + * @throws \InvalidArgumentException When the inheritance type does not exist. + */ + protected function getInheritanceTypeString($type) + { + if ( ! isset(static::$inheritanceTypeMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type)); + } + + return static::$inheritanceTypeMap[$type]; + } + + /** + * @param integer $type The policy used for change-tracking for the mapped class. + * + * @return string The literal string for the change-tracking type. + * + * @throws \InvalidArgumentException When the change-tracking type does not exist. + */ + protected function getChangeTrackingPolicyString($type) + { + if ( ! isset(static::$changeTrackingPolicyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type)); + } + + return static::$changeTrackingPolicyMap[$type]; + } + + /** + * @param integer $type The generator to use for the mapped class. + * + * @return string The literal string for the generator type. + * + * @throws \InvalidArgumentException When the generator type does not exist. + */ + protected function getIdGeneratorTypeString($type) + { + if ( ! isset(static::$generatorStrategyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type)); + } + + return static::$generatorStrategyMap[$type]; + } + + /** + * Exports (nested) option elements. + * + * @param array $options + */ + private function exportTableOptions(array $options) + { + $optionsStr = array(); + + foreach($options as $name => $option) { + if (is_array($option)) { + $optionsStr[] = '"' . $name . '"={' . $this->exportTableOptions($option) . '}'; + } else { + $optionsStr[] = '"' . $name . '"="' . (string) $option . '"'; + } + } + + return implode(',', $optionsStr); + } +} 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 00000000000..f431588fb09 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php @@ -0,0 +1,171 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +/** + * Class to generate entity repository classes + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityRepositoryGenerator +{ + private $repositoryName; + + protected static $_template = +' + +/** + * + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class extends +{ +} +'; + + /** + * @param string $fullClassName + * + * @return string + */ + public function generateEntityRepositoryClass($fullClassName) + { + $variables = array( + '' => $this->generateEntityRepositoryNamespace($fullClassName), + '' => $this->generateEntityRepositoryName($fullClassName), + '' => $this->generateClassName($fullClassName) + ); + + return str_replace(array_keys($variables), array_values($variables), self::$_template); + } + + /** + * Generates the namespace, if class do not have namespace, return empty string instead. + * + * @param string $fullClassName + * + * @return string $namespace + */ + private function getClassNamespace($fullClassName) + { + $namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\')); + + return $namespace; + } + + /** + * Generates the class name + * + * @param string $fullClassName + * + * @return string + */ + private function generateClassName($fullClassName) + { + $namespace = $this->getClassNamespace($fullClassName); + + $className = $fullClassName; + + if ($namespace) { + $className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName)); + } + + return $className; + } + + /** + * Generates the namespace statement, if class do not have namespace, return empty string instead. + * + * @param string $fullClassName The full repository class name. + * + * @return string $namespace + */ + private function generateEntityRepositoryNamespace($fullClassName) + { + $namespace = $this->getClassNamespace($fullClassName); + + return $namespace ? 'namespace ' . $namespace . ';' : ''; + } + + /** + * @param string $fullClassName + * + * @return string $repositoryName + */ + private function generateEntityRepositoryName($fullClassName) + { + $namespace = $this->getClassNamespace($fullClassName); + + $repositoryName = $this->repositoryName ?: 'Doctrine\ORM\EntityRepository'; + + if ($namespace && $repositoryName[0] !== '\\') { + $repositoryName = '\\' . $repositoryName; + } + + return $repositoryName; + } + + /** + * @param string $fullClassName + * @param string $outputDirectory + * + * @return void + */ + 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, 0775, true); + } + + if ( ! file_exists($path)) { + file_put_contents($path, $code); + chmod($path, 0664); + } + } + + /** + * @param string $repositoryName + * + * @return \Doctrine\ORM\Tools\EntityRepositoryGenerator + */ + public function setDefaultRepositoryName($repositoryName) + { + $this->repositoryName = $repositoryName; + + return $this; + } + +} 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 00000000000..ed03e32d9dc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Event Args used for the Events::postGenerateSchema event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * @param EntityManagerInterface $em + * @param Schema $schema + */ + public function __construct(EntityManagerInterface $em, Schema $schema) + { + $this->em = $em; + $this->schema = $schema; + } + + /** + * @return EntityManagerInterface + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * @return Schema + */ + public function getSchema() + { + return $this->schema; + } +} 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 00000000000..e2c38f9c592 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php @@ -0,0 +1,86 @@ +. + */ +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\Common\EventArgs; +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/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaTableEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $classTable; + + /** + * @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; + } +} 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 00000000000..1ea2a3735a9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export; + +/** + * Class used for converting your mapping information between the + * supported formats: yaml, xml, and php/annotation. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class ClassMetadataExporter +{ + /** + * @var array + */ + 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' + ); + + /** + * Registers a new exporter driver class under a specified name. + * + * @param string $name + * @param string $class + * + * @return void + */ + public static function registerExportDriver($name, $class) + { + self::$_exporterDrivers[$name] = $class; + } + + /** + * Gets an exporter driver instance. + * + * @param string $type The type to get (yml, xml, etc.). + * @param string|null $dest The directory where the exporter will export to. + * + * @return Driver\AbstractExporter + * + * @throws ExportException + */ + public function getExporter($type, $dest = null) + { + if ( ! isset(self::$_exporterDrivers[$type])) { + throw ExportException::invalidExporterDriverType($type); + } + + $class = self::$_exporterDrivers[$type]; + + return new $class($dest); + } +} 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 00000000000..b2ed435bc42 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -0,0 +1,269 @@ +. + */ + +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. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +abstract class AbstractExporter +{ + /** + * @var array + */ + protected $_metadata = array(); + + /** + * @var string|null + */ + protected $_outputDir; + + /** + * @var string|null + */ + protected $_extension; + + /** + * @var bool + */ + protected $_overwriteExistingFiles = false; + + /** + * @param string|null $dir + */ + public function __construct($dir = null) + { + $this->_outputDir = $dir; + } + + /** + * @param bool $overwrite + * + * @return void + */ + public function setOverwriteExistingFiles($overwrite) + { + $this->_overwriteExistingFiles = $overwrite; + } + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + abstract public function exportClassMetadata(ClassMetadataInfo $metadata); + + /** + * Sets the array of ClassMetadataInfo instances to export. + * + * @param array $metadata + * + * @return void + */ + public function setMetadata(array $metadata) + { + $this->_metadata = $metadata; + } + + /** + * Gets the extension used to generated the path to a class. + * + * @return string|null + */ + public function getExtension() + { + return $this->_extension; + } + + /** + * Sets 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; + } + + /** + * Exports each ClassMetadata instance to a single Doctrine Mapping file + * named after the entity. + * + * @return void + * + * @throws \Doctrine\ORM\Tools\Export\ExportException + */ + public function export() + { + if ( ! is_dir($this->_outputDir)) { + mkdir($this->_outputDir, 0775, 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, 0775, true); + } + if (file_exists($path) && !$this->_overwriteExistingFiles) { + throw ExportException::attemptOverwriteExistingFile($path); + } + file_put_contents($path, $output); + chmod($path, 0664); + } + } + } + + /** + * Generates the path to write the class for the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '.', $metadata->name) . $this->_extension; + } + + /** + * Sets 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; + } + + /** + * @param int $type + * + * @return string + */ + protected 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'; + } + } + + /** + * @param int $mode + * + * @return string + */ + protected function _getFetchModeString($mode) + { + switch ($mode) { + case ClassMetadataInfo::FETCH_EAGER: + return 'EAGER'; + + case ClassMetadataInfo::FETCH_EXTRA_LAZY: + return 'EXTRA_LAZY'; + + case ClassMetadataInfo::FETCH_LAZY: + return 'LAZY'; + } + } + + /** + * @param int $policy + * + * @return string + */ + protected 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'; + } + } + + /** + * @param int $type + * + * @return string + */ + protected 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_UUID: + return 'UUID'; + + case ClassMetadataInfo::GENERATOR_TYPE_CUSTOM: + return 'CUSTOM'; + } + } +} 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 00000000000..044a1da53a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\EntityGenerator; + +/** + * ClassMetadata exporter for PHP classes with annotations. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class AnnotationExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.php'; + + /** + * @var EntityGenerator|null + */ + private $_entityGenerator; + + /** + * {@inheritdoc} + */ + 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); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + * + * @return string + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension; + } + + /** + * @param \Doctrine\ORM\Tools\EntityGenerator $entityGenerator + * + * @return void + */ + public function setEntityGenerator(EntityGenerator $entityGenerator) + { + $this->_entityGenerator = $entityGenerator; + } +} 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 00000000000..29eb37afc81 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php @@ -0,0 +1,177 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for PHP code. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class PhpExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.php'; + + /** + * {@inheritdoc} + */ + 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]); + } + } + + if (count($cascade) === 5) { + $cascade = array('all'); + } + + $associationMappingArray = array( + 'fieldName' => $associationMapping['fieldName'], + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + if (isset($associationMapping['fetch'])) { + $associationMappingArray['fetch'] = $associationMapping['fetch']; + } + + 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); + } elseif ($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); + } elseif ($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); + } + + /** + * @param mixed $var + * + * @return string + */ + 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 00000000000..6b53cbbaf32 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -0,0 +1,453 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine XML mapping files. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class XmlExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.dcm.xml'; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $xml = new \SimpleXmlElement(""); + + 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 ($metadata->inheritanceType && $metadata->inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $root->addAttribute('inheritance-type', $this->_getInheritanceTypeString($metadata->inheritanceType)); + } + + if (isset($metadata->table['options'])) { + $optionsXml = $root->addChild('options'); + + $this->exportTableOptions($optionsXml, $metadata->table['options']); + } + + if ($metadata->discriminatorColumn) { + $discriminatorColumnXml = $root->addChild('discriminator-column'); + $discriminatorColumnXml->addAttribute('name', $metadata->discriminatorColumn['name']); + $discriminatorColumnXml->addAttribute('type', $metadata->discriminatorColumn['type']); + + if (isset($metadata->discriminatorColumn['length'])) { + $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); + } + } + + $trackingPolicy = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); + + if ( $trackingPolicy != 'DEFERRED_IMPLICIT') { + $root->addChild('change-tracking-policy', $trackingPolicy); + } + + 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($index['flags'])) { + $indexXml->addAttribute('flags', implode(',', $index['flags'])); + } + } + } + + if (isset($metadata->table['uniqueConstraints'])) { + $uniqueConstraintsXml = $root->addChild('unique-constraints'); + + foreach ($metadata->table['uniqueConstraints'] as $name => $unique) { + $uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint'); + $uniqueConstraintXml->addAttribute('name', $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]); + } + } + + foreach ($metadata->associationMappings as $name => $assoc) { + if (isset($assoc['id']) && $assoc['id']) { + $id[$name] = array( + 'fieldName' => $name, + 'associationKey' => true + ); + } + } + + 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']); + + if (isset($field['type'])) { + $idXml->addAttribute('type', $field['type']); + } + + if (isset($field['columnName'])) { + $idXml->addAttribute('column', $field['columnName']); + } + + if (isset($field['length'])) { + $idXml->addAttribute('length', $field['length']); + } + + 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); + + $this->exportSequenceInformation($idXml, $metadata); + } + } + } + + 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'] ? 'true' : 'false'); + } + + if (isset($field['options'])) { + $optionsXml = $fieldXml->addChild('options'); + foreach ($field['options'] as $key => $value) { + $optionXml = $optionsXml->addChild('option', $value); + $optionXml->addAttribute('name', $key); + } + } + + if (isset($field['version'])) { + $fieldXml->addAttribute('version', $field['version']); + } + + if (isset($field['columnDefinition'])) { + $fieldXml->addAttribute('column-definition', $field['columnDefinition']); + } + + if (isset($field['nullable'])) { + $fieldXml->addAttribute('nullable', $field['nullable'] ? 'true' : 'false'); + } + } + } + + $orderMap = array( + ClassMetadataInfo::ONE_TO_ONE, + ClassMetadataInfo::ONE_TO_MANY, + ClassMetadataInfo::MANY_TO_ONE, + ClassMetadataInfo::MANY_TO_MANY, + ); + + uasort($metadata->associationMappings, function($m1, $m2) use (&$orderMap){ + $a1 = array_search($m1['type'], $orderMap); + $a2 = array_search($m2['type'], $orderMap); + + return strcmp($a1, $a2); + }); + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) { + $associationMappingXml = $root->addChild('one-to-one'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_ONE) { + $associationMappingXml = $root->addChild('many-to-one'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $associationMappingXml = $root->addChild('one-to-many'); + } elseif ($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['indexBy'])) { + $associationMappingXml->addAttribute('index-by', $associationMapping['indexBy']); + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval'] !== false) { + $associationMappingXml->addAttribute('orphan-removal', 'true'); + } + + if (isset($associationMapping['fetch'])) { + $associationMappingXml->addAttribute('fetch', $this->_getFetchModeString($associationMapping['fetch'])); + } + + $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($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); + } + } + } + + if (isset($metadata->lifecycleCallbacks) && count($metadata->lifecycleCallbacks)>0) { + $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); + } + + /** + * Exports (nested) option elements. + * + * @param \SimpleXMLElement $parentXml + * @param array $options + */ + private function exportTableOptions(\SimpleXMLElement $parentXml, array $options) + { + foreach ($options as $name => $option) { + $isArray = is_array($option); + $optionXml = $isArray + ? $parentXml->addChild('option') + : $parentXml->addChild('option', (string) $option); + + $optionXml->addAttribute('name', (string) $name); + + if ($isArray) { + $this->exportTableOptions($optionXml, $option); + } + } + } + + /** + * Export sequence information (if available/configured) into the current identifier XML node + * + * @param \SimpleXMLElement $identifierXmlNode + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function exportSequenceInformation(\SimpleXMLElement $identifierXmlNode, ClassMetadataInfo $metadata) + { + $sequenceDefinition = $metadata->sequenceGeneratorDefinition; + + if (! ($metadata->generatorType === ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE && $sequenceDefinition)) { + return; + } + + $sequenceGeneratorXml = $identifierXmlNode->addChild('sequence-generator'); + + $sequenceGeneratorXml->addAttribute('sequence-name', $sequenceDefinition['sequenceName']); + $sequenceGeneratorXml->addAttribute('allocation-size', $sequenceDefinition['allocationSize']); + $sequenceGeneratorXml->addAttribute('initial-value', $sequenceDefinition['initialValue']); + } + + /** + * @param \SimpleXMLElement $simpleXml + * + * @return string $xml + */ + private function _asXml($simpleXml) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadXML($simpleXml->asXML()); + $dom->formatOutput = true; + + return $dom->saveXML(); + } +} 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 00000000000..177cebfabbf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -0,0 +1,235 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Symfony\Component\Yaml\Yaml; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine YAML mapping files. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class YamlExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritdoc} + */ + 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']; + } + + if (isset($metadata->table['options'])) { + $array['options'] = $metadata->table['options']; + } + + $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; + } + + $array['id'] = $ids; + + if ($fieldMappings) { + if ( ! isset($array['fields'])) { + $array['fields'] = array(); + } + $array['fields'] = array_merge($array['fields'], $fieldMappings); + } + + 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 (isset($associationMapping['fetch'])) { + $associationMappingArray['fetch'] = $this->_getFetchModeString($associationMapping['fetch']); + } + + if (isset($mapping['id']) && $mapping['id'] === true) { + $array['id'][$name]['associationKey'] = true; + } + + 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; + } + } elseif ($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; + } elseif ($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 $this->yamlDump(array($metadata->name => $array), 10); + } + + /** + * Dumps a PHP array to a YAML string. + * + * The yamlDump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param integer $inline [optional] The level where you switch to inline YAML + * + * @return string A YAML string representing the original PHP array + */ + protected function yamlDump($array, $inline = 2) + { + return Yaml::dump($array, $inline); + } +} 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 00000000000..89ddfe22eb0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php @@ -0,0 +1,38 @@ + FROM ()) + * + * Works with composite keys but cannot deal with queries that have multiple + * root entities (e.g. `SELECT f, b from Foo, Bar`) + * + * @author Sander Marechal + */ +class CountOutputWalker extends SqlWalker +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $rsm; + + /** + * @var array + */ + private $queryComponents; + + /** + * Constructor. + * + * Stores various parameters that are otherwise unavailable + * because Doctrine\ORM\Query\SqlWalker keeps everything private without + * accessors. + * + * @param \Doctrine\ORM\Query $query + * @param \Doctrine\ORM\Query\ParserResult $parserResult + * @param array $queryComponents + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); + $this->rsm = $parserResult->getResultSetMapping(); + $this->queryComponents = $queryComponents; + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a COUNT (SELECT DISTINCT). + * + * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL) + * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery + * that will most likely be executed next can be read from the native SQL cache. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($this->platform->getName() === "mssql") { + $AST->orderByClause = null; + } + + $sql = parent::walkSelectStatement($AST); + + // Find out the SQL alias of the identifier column of the root entity + // It may be possible to make this work with multiple root entities but that + // would probably require issuing multiple queries or doing a UNION SELECT + // so for now, It's not supported. + + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + if (count($from) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $this->queryComponents[$rootAlias]['metadata']; + $rootIdentifier = $rootClass->identifier; + + // For every identifier, find out the SQL alias by combing through the ResultSetMapping + $sqlIdentifier = array(); + foreach ($rootIdentifier as $property) { + if (isset($rootClass->fieldMappings[$property])) { + foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + + if (isset($rootClass->associationMappings[$property])) { + $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + + foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + } + + if (count($rootIdentifier) != count($sqlIdentifier)) { + throw new \RuntimeException(sprintf( + 'Not all identifier properties can be found in the ResultSetMapping: %s', + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + )); + } + + // Build the counter query + return sprintf('SELECT %s AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table', + $this->platform->getCountExpression('*'), + implode(', ', $sqlIdentifier), + $sql + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php new file mode 100644 index 00000000000..ff0c6b36331 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php @@ -0,0 +1,88 @@ + + * @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 + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($AST->havingClause) { + throw new \RuntimeException('Cannot count query that uses a HAVING clause. Use the output walkers for pagination'); + } + + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + + if (count($from) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($rootClass->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, + $identifierFieldName + ); + $pathExpression->type = $pathType; + + $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/LimitSubqueryOutputWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php new file mode 100644 index 00000000000..b1d7bd9eb8d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -0,0 +1,589 @@ + FROM () LIMIT x OFFSET y + * + * Works with composite keys but cannot deal with queries that have multiple + * root entities (e.g. `SELECT f, b from Foo, Bar`) + * + * @author Sander Marechal + */ +class LimitSubqueryOutputWalker extends SqlWalker +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $rsm; + + /** + * @var array + */ + private $queryComponents; + + /** + * @var int + */ + private $firstResult; + + /** + * @var int + */ + private $maxResults; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * @var array + */ + private $orderByPathExpressions = []; + + /** + * @var bool We don't want to add path expressions from sub-selects into the select clause of the containing query. + * This state flag simply keeps track on whether we are walking on a subquery or not + */ + private $inSubSelect = false; + + /** + * Constructor. + * + * Stores various parameters that are otherwise unavailable + * because Doctrine\ORM\Query\SqlWalker keeps everything private without + * accessors. + * + * @param \Doctrine\ORM\Query $query + * @param \Doctrine\ORM\Query\ParserResult $parserResult + * @param array $queryComponents + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); + $this->rsm = $parserResult->getResultSetMapping(); + $this->queryComponents = $queryComponents; + + // Reset limit and offset + $this->firstResult = $query->getFirstResult(); + $this->maxResults = $query->getMaxResults(); + $query->setFirstResult(null)->setMaxResults(null); + + $this->em = $query->getEntityManager(); + $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Check if the platform supports the ROW_NUMBER window function. + * + * @return bool + */ + private function platformSupportsRowNumber() + { + return $this->platform instanceof PostgreSqlPlatform + || $this->platform instanceof SQLServerPlatform + || $this->platform instanceof OraclePlatform + || $this->platform instanceof SQLAnywherePlatform + || $this->platform instanceof DB2Platform + || (method_exists($this->platform, 'supportsRowNumberFunction') + && $this->platform->supportsRowNumberFunction()); + } + + /** + * Rebuilds a select statement's order by clause for use in a + * ROW_NUMBER() OVER() expression. + * + * @param SelectStatement $AST + */ + private function rebuildOrderByForRowNumber(SelectStatement $AST) + { + $orderByClause = $AST->orderByClause; + $selectAliasToExpressionMap = []; + // Get any aliases that are available for select expressions. + foreach ($AST->selectClause->selectExpressions as $selectExpression) { + $selectAliasToExpressionMap[$selectExpression->fieldIdentificationVariable] = $selectExpression->expression; + } + + // Rebuild string orderby expressions to use the select expression they're referencing + foreach ($orderByClause->orderByItems as $orderByItem) { + if (is_string($orderByItem->expression) && isset($selectAliasToExpressionMap[$orderByItem->expression])) { + $orderByItem->expression = $selectAliasToExpressionMap[$orderByItem->expression]; + } + } + $func = new RowNumberOverFunction('dctrn_rownum'); + $func->orderByClause = $AST->orderByClause; + $AST->selectClause->selectExpressions[] = new SelectExpression($func, 'dctrn_rownum', true); + + // No need for an order by clause, we'll order by rownum in the outer query. + $AST->orderByClause = null; + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($this->platformSupportsRowNumber()) { + return $this->walkSelectStatementWithRowNumber($AST); + } + return $this->walkSelectStatementWithoutRowNumber($AST); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. + * This method is for use with platforms which support ROW_NUMBER. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatementWithRowNumber(SelectStatement $AST) + { + $hasOrderBy = false; + $outerOrderBy = ' ORDER BY dctrn_minrownum ASC'; + $orderGroupBy = ''; + if ($AST->orderByClause instanceof OrderByClause) { + $hasOrderBy = true; + $this->rebuildOrderByForRowNumber($AST); + } + + $innerSql = $this->getInnerSQL($AST); + + $sqlIdentifier = $this->getSQLIdentifier($AST); + + if ($hasOrderBy) { + $orderGroupBy = ' GROUP BY ' . implode(', ', $sqlIdentifier); + $sqlIdentifier[] = 'MIN(' . $this->walkResultVariable('dctrn_rownum') . ') AS dctrn_minrownum'; + } + + // Build the counter query + $sql = sprintf( + 'SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), + $innerSql + ); + + if ($hasOrderBy) { + $sql .= $orderGroupBy . $outerOrderBy; + } + + // Apply the limit and offset. + $sql = $this->platform->modifyLimitQuery( + $sql, + $this->maxResults, + $this->firstResult + ); + + // Add the columns to the ResultSetMapping. It's not really nice but + // it works. Preferably I'd clear the RSM or simply create a new one + // but that is not possible from inside the output walker, so we dirty + // up the one we have. + foreach ($sqlIdentifier as $property => $alias) { + $this->rsm->addScalarResult($alias, $property); + } + + return $sql; + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. + * This method is for platforms which DO NOT support ROW_NUMBER. + * + * @param SelectStatement $AST + * @param bool $addMissingItemsFromOrderByToSelect + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, $addMissingItemsFromOrderByToSelect = true) + { + // We don't want to call this recursively! + if ($AST->orderByClause instanceof OrderByClause && $addMissingItemsFromOrderByToSelect) { + // In the case of ordering a query by columns from joined tables, we + // must add those columns to the select clause of the query BEFORE + // the SQL is generated. + $this->addMissingItemsFromOrderByToSelect($AST); + } + + // Remove order by clause from the inner query + // It will be re-appended in the outer select generated by this method + $orderByClause = $AST->orderByClause; + $AST->orderByClause = null; + + $innerSql = $this->getInnerSQL($AST); + + $sqlIdentifier = $this->getSQLIdentifier($AST); + + // Build the counter query + $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), $innerSql); + + // http://www.doctrine-project.org/jira/browse/DDC-1958 + $sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause); + + // Apply the limit and offset. + $sql = $this->platform->modifyLimitQuery( + $sql, $this->maxResults, $this->firstResult + ); + + // Add the columns to the ResultSetMapping. It's not really nice but + // it works. Preferably I'd clear the RSM or simply create a new one + // but that is not possible from inside the output walker, so we dirty + // up the one we have. + foreach ($sqlIdentifier as $property => $alias) { + $this->rsm->addScalarResult($alias, $property); + } + + // Restore orderByClause + $AST->orderByClause = $orderByClause; + + return $sql; + } + + /** + * Finds all PathExpressions in an AST's OrderByClause, and ensures that + * the referenced fields are present in the SelectClause of the passed AST. + * + * @param SelectStatement $AST + */ + private function addMissingItemsFromOrderByToSelect(SelectStatement $AST) + { + $this->orderByPathExpressions = []; + + // We need to do this in another walker because otherwise we'll end up + // polluting the state of this one. + $walker = clone $this; + + // This will populate $orderByPathExpressions via + // LimitSubqueryOutputWalker::walkPathExpression, which will be called + // as the select statement is walked. We'll end up with an array of all + // path expressions referenced in the query. + $walker->walkSelectStatementWithoutRowNumber($AST, false); + $orderByPathExpressions = $walker->getOrderByPathExpressions(); + + // Get a map of referenced identifiers to field names. + $selects = []; + foreach ($orderByPathExpressions as $pathExpression) { + $idVar = $pathExpression->identificationVariable; + $field = $pathExpression->field; + if (!isset($selects[$idVar])) { + $selects[$idVar] = []; + } + $selects[$idVar][$field] = true; + } + + // Loop the select clause of the AST and exclude items from $select + // that are already being selected in the query. + foreach ($AST->selectClause->selectExpressions as $selectExpression) { + if ($selectExpression instanceof SelectExpression) { + $idVar = $selectExpression->expression; + if (!is_string($idVar)) { + continue; + } + $field = $selectExpression->fieldIdentificationVariable; + if ($field === null) { + // No need to add this select, as we're already fetching the whole object. + unset($selects[$idVar]); + } else { + unset($selects[$idVar][$field]); + } + } + } + + // Add select items which were not excluded to the AST's select clause. + foreach ($selects as $idVar => $fields) { + $AST->selectClause->selectExpressions[] = new SelectExpression(new PartialObjectExpression($idVar, array_keys($fields)), null, true); + } + } + + /** + * Generates new SQL for statements with an order by clause + * + * @param array $sqlIdentifier + * @param string $innerSql + * @param string $sql + * @param OrderByClause $orderByClause + * + * @return string + */ + private function preserveSqlOrdering(array $sqlIdentifier, $innerSql, $sql, $orderByClause) + { + // If the sql statement has an order by clause, we need to wrap it in a new select distinct + // statement + if (! $orderByClause instanceof OrderByClause) { + return $sql; + } + + // Rebuild the order by clause to work in the scope of the new select statement + /* @var array $orderBy an array of rebuilt order by items */ + $orderBy = $this->rebuildOrderByClauseForOuterScope($orderByClause); + + // Build the select distinct statement + $sql = sprintf( + 'SELECT DISTINCT %s FROM (%s) dctrn_result ORDER BY %s', + implode(', ', $sqlIdentifier), + $innerSql, + implode(', ', $orderBy) + ); + + return $sql; + } + + /** + * Generates a new order by clause that works in the scope of a select query wrapping the original + * + * @param OrderByClause $orderByClause + * @return array + */ + private function rebuildOrderByClauseForOuterScope(OrderByClause $orderByClause) + { + $dqlAliasToSqlTableAliasMap + = $searchPatterns + = $replacements + = $dqlAliasToClassMap + = $selectListAdditions + = $orderByItems + = []; + + // Generate DQL alias -> SQL table alias mapping + foreach(array_keys($this->rsm->aliasMap) as $dqlAlias) { + $dqlAliasToClassMap[$dqlAlias] = $class = $this->queryComponents[$dqlAlias]['metadata']; + $dqlAliasToSqlTableAliasMap[$dqlAlias] = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + } + + // Pattern to find table path expressions in the order by clause + $fieldSearchPattern = '/(?rsm->fieldMappings as $fieldAlias => $fieldName) { + $dqlAliasForFieldAlias = $this->rsm->columnOwnerMap[$fieldAlias]; + $class = $dqlAliasToClassMap[$dqlAliasForFieldAlias]; + + // If the field is from a joined child table, we won't be ordering + // on it. + if (!isset($class->fieldMappings[$fieldName])) { + continue; + } + + $fieldMapping = $class->fieldMappings[$fieldName]; + + // Get the proper column name as will appear in the select list + $columnName = $this->quoteStrategy->getColumnName( + $fieldName, + $dqlAliasToClassMap[$dqlAliasForFieldAlias], + $this->em->getConnection()->getDatabasePlatform() + ); + + // Get the SQL table alias for the entity and field + $sqlTableAliasForFieldAlias = $dqlAliasToSqlTableAliasMap[$dqlAliasForFieldAlias]; + if (isset($fieldMapping['declared']) && $fieldMapping['declared'] !== $class->name) { + // Field was declared in a parent class, so we need to get the proper SQL table alias + // for the joined parent table. + $otherClassMetadata = $this->em->getClassMetadata($fieldMapping['declared']); + if (!$otherClassMetadata->isMappedSuperclass) { + $sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias); + + } + } + + // Compose search/replace patterns + $searchPatterns[] = sprintf($fieldSearchPattern, $sqlTableAliasForFieldAlias, $columnName); + $replacements[] = $fieldAlias; + } + + foreach($orderByClause->orderByItems as $orderByItem) { + // Walk order by item to get string representation of it + $orderByItemString = $this->walkOrderByItem($orderByItem); + + // Replace path expressions in the order by clause with their column alias + $orderByItemString = preg_replace($searchPatterns, $replacements, $orderByItemString); + + $orderByItems[] = $orderByItemString; + } + + return $orderByItems; + } + + /** + * getter for $orderByPathExpressions + * + * @return array + */ + public function getOrderByPathExpressions() + { + return $this->orderByPathExpressions; + } + + /** + * @param SelectStatement $AST + * + * @return string + * + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\Query\QueryException + */ + private function getInnerSQL(SelectStatement $AST) + { + // Set every select expression as visible(hidden = false) to + // make $AST have scalar mappings properly - this is relevant for referencing selected + // fields from outside the subquery, for example in the ORDER BY segment + $hiddens = []; + + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $hiddens[$idx] = $expr->hiddenAliasResultVariable; + $expr->hiddenAliasResultVariable = false; + } + + $innerSql = parent::walkSelectStatement($AST); + + // Restore hiddens + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $expr->hiddenAliasResultVariable = $hiddens[$idx]; + } + + return $innerSql; + } + + /** + * @param SelectStatement $AST + * + * @return array + */ + private function getSQLIdentifier(SelectStatement $AST) + { + // Find out the SQL alias of the identifier column of the root entity. + // It may be possible to make this work with multiple root entities but that + // would probably require issuing multiple queries or doing a UNION SELECT. + // So for now, it's not supported. + + // Get the root entity and alias from the AST fromClause. + $from = $AST->fromClause->identificationVariableDeclarations; + if (count($from) !== 1) { + throw new \RuntimeException('Cannot count query which selects two FROM components, cannot make distinction'); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $this->queryComponents[$rootAlias]['metadata']; + $rootIdentifier = $rootClass->identifier; + + // For every identifier, find out the SQL alias by combing through the ResultSetMapping + $sqlIdentifier = []; + foreach ($rootIdentifier as $property) { + if (isset($rootClass->fieldMappings[$property])) { + foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + + if (isset($rootClass->associationMappings[$property])) { + $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + + foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + } + + if (count($sqlIdentifier) === 0) { + throw new \RuntimeException('The Paginator does not support Queries which only yield ScalarResults.'); + } + + if (count($rootIdentifier) != count($sqlIdentifier)) { + throw new \RuntimeException(sprintf( + 'Not all identifier properties can be found in the ResultSetMapping: %s', + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + )); + } + + return $sqlIdentifier; + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + if (!$this->inSubSelect && !$this->platformSupportsRowNumber() && !in_array($pathExpr, $this->orderByPathExpressions)) { + $this->orderByPathExpressions[] = $pathExpr; + } + + return parent::walkPathExpression($pathExpr); + } + + /** + * {@inheritdoc} + */ + public function walkSubSelect($subselect) + { + $this->inSubSelect = true; + + $sql = parent::walkSubselect($subselect); + + $this->inSubSelect = false; + + return $sql; + } +} 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 00000000000..e65cfcaeb6e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php @@ -0,0 +1,175 @@ + + * @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; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\TreeWalkerAdapter; +use Doctrine\ORM\Query\AST\Functions\IdentityFunction; +use Doctrine\ORM\Query\AST\PathExpression; +use Doctrine\ORM\Query\AST\SelectExpression; +use Doctrine\ORM\Query\AST\SelectStatement; + +/** + * 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'; + + /** + * Counter for generating unique order column aliases. + * + * @var int + */ + private $_aliasCounter = 0; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids + * of the root Entity. + * + * @param SelectStatement $AST + * + * @return void + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $selectExpressions = array(); + + $this->validate($AST); + + foreach ($queryComponents as $dqlAlias => $qComp) { + // Preserve mixed data in query for ordering. + if (isset($qComp['resultVariable'])) { + $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias); + continue; + } + } + + $identifier = $rootClass->getSingleIdentifierFieldName(); + + if (isset($rootClass->associationMappings[$identifier])) { + throw new \RuntimeException("Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator."); + } + + $this->_getQuery()->setHint( + self::IDENTIFIER_TYPE, + Type::getType($rootClass->getTypeOfField($identifier)) + ); + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $rootAlias, + $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) { + continue; + } + + $AST->selectClause->selectExpressions[] = new SelectExpression( + $this->createSelectExpressionItem($item->expression), + '_dctrn_ord' . $this->_aliasCounter++ + ); + } + } + + $AST->selectClause->isDistinct = true; + } + + /** + * Validate the AST to ensure that this walker is able to properly manipulate it. + * + * @param SelectStatement $AST + */ + private function validate(SelectStatement $AST) + { + // Prevent LimitSubqueryWalker from being used with queries that include + // a limit, a fetched to-many join, and an order by condition that + // references a column from the fetch joined table. + $queryComponents = $this->getQueryComponents(); + $query = $this->_getQuery(); + $from = $AST->fromClause->identificationVariableDeclarations; + $fromRoot = reset($from); + + if ($query instanceof Query + && $query->getMaxResults() + && $AST->orderByClause + && count($fromRoot->joins)) { + // Check each orderby item. + // TODO: check complex orderby items too... + foreach ($AST->orderByClause->orderByItems as $orderByItem) { + $expression = $orderByItem->expression; + if ($orderByItem->expression instanceof PathExpression + && isset($queryComponents[$expression->identificationVariable])) { + $queryComponent = $queryComponents[$expression->identificationVariable]; + if (isset($queryComponent['parent']) + && $queryComponent['relation']['type'] & ClassMetadataInfo::TO_MANY) { + throw new \RuntimeException("Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers."); + } + } + } + } + } + + /** + * Retrieve either an IdentityFunction (IDENTITY(u.assoc)) or a state field (u.name). + * + * @param \Doctrine\ORM\Query\AST\PathExpression $pathExpression + * + * @return \Doctrine\ORM\Query\AST\Functions\IdentityFunction + */ + private function createSelectExpressionItem(PathExpression $pathExpression) + { + if ($pathExpression->type === PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) { + $identity = new IdentityFunction('identity'); + + $identity->pathExpression = clone $pathExpression; + + return $identity; + } + + return clone $pathExpression; + } +} 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 00000000000..c5b0b368600 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -0,0 +1,279 @@ +. + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\NoResultException; + +/** + * 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 bool|null + */ + private $useOutputWalkers; + + /** + * @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; + } + + /** + * Returns whether the paginator will use an output walker. + * + * @return bool|null + */ + public function getUseOutputWalkers() + { + return $this->useOutputWalkers; + } + + /** + * Sets whether the paginator will use an output walker. + * + * @param bool|null $useOutputWalkers + * + * @return $this + */ + public function setUseOutputWalkers($useOutputWalkers) + { + $this->useOutputWalkers = $useOutputWalkers; + return $this; + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ($this->count === null) { + try { + $this->count = array_sum(array_map('current', $this->getCountQuery()->getScalarResult())); + } 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); + + if ($this->useOutputWalker($subQuery)) { + $subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + } else { + $this->appendTreeWalker($subQuery, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'); + } + + $subQuery->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) { + return new \ArrayIterator(array()); + } + + $this->appendTreeWalker($whereInQuery, 'Doctrine\ORM\Tools\Pagination\WhereInWalker'); + $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids)); + $whereInQuery->setFirstResult(null)->setMaxResults(null); + $whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $ids); + $whereInQuery->setCacheable($this->query->isCacheable()); + + $result = $whereInQuery->getResult($this->query->getHydrationMode()); + } else { + $result = $this->cloneQuery($this->query) + ->setMaxResults($length) + ->setFirstResult($offset) + ->setCacheable($this->query->isCacheable()) + ->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(clone $query->getParameters()); + $cloneQuery->setCacheable(false); + + foreach ($query->getHints() as $name => $value) { + $cloneQuery->setHint($name, $value); + } + + return $cloneQuery; + } + + /** + * Determines whether to use an output walker for the query. + * + * @param Query $query The query. + * + * @return bool + */ + private function useOutputWalker(Query $query) + { + if ($this->useOutputWalkers === null) { + return (Boolean) $query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER) == false; + } + + return $this->useOutputWalkers; + } + + /** + * Appends a custom tree walker to the tree walkers hint. + * + * @param Query $query + * @param string $walkerClass + */ + private function appendTreeWalker(Query $query, $walkerClass) + { + $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS); + + if ($hints === false) { + $hints = array(); + } + + $hints[] = $walkerClass; + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $hints); + } + + /** + * Returns Query prepared to count. + * + * @return Query + */ + private function getCountQuery() + { + /* @var $countQuery Query */ + $countQuery = $this->cloneQuery($this->query); + + if ( ! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) { + $countQuery->setHint(CountWalker::HINT_DISTINCT, true); + } + + if ($this->useOutputWalker($countQuery)) { + $platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win + + $rsm = new ResultSetMapping(); + $rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count'); + + $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker'); + $countQuery->setResultSetMapping($rsm); + } else { + $this->appendTreeWalker($countQuery, 'Doctrine\ORM\Tools\Pagination\CountWalker'); + } + + $countQuery->setFirstResult(null)->setMaxResults(null); + + $parser = new Parser($countQuery); + $parameterMappings = $parser->parse()->getParameterMappings(); + /* @var $parameters \Doctrine\Common\Collections\Collection|\Doctrine\ORM\Query\Parameter[] */ + $parameters = $countQuery->getParameters(); + + foreach ($parameters as $key => $parameter) { + $parameterName = $parameter->getName(); + + if( ! (isset($parameterMappings[$parameterName]) || array_key_exists($parameterName, $parameterMappings))) { + unset($parameters[$key]); + } + } + + $countQuery->setParameters($parameters); + + return $countQuery; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/RowNumberOverFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/RowNumberOverFunction.php new file mode 100644 index 00000000000..ca3e57bea0a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/RowNumberOverFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\ORMException; +use Doctrine\ORM\Query\AST\Functions\FunctionNode; + + +/** + * RowNumberOverFunction + * + * Provides ROW_NUMBER() OVER(ORDER BY...) construct for use in LimitSubqueryOutputWalker + * + * @since 2.5 + * @author Bill Schaller + */ +class RowNumberOverFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\OrderByClause + */ + public $orderByClause; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'ROW_NUMBER() OVER(' . trim($sqlWalker->walkOrderByClause( + $this->orderByClause + )) . ')'; + } + + /** + * @override + * + * @throws ORMException + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + throw new ORMException("The RowNumberOverFunction is not intended for, nor is it enabled for use in DQL."); + } +} 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 00000000000..9f42a1eea7a --- /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; +use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; +use Doctrine\ORM\Query\TreeWalkerAdapter; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\AST\PathExpression; +use Doctrine\ORM\Query\AST\InExpression; +use Doctrine\ORM\Query\AST\NullComparisonExpression; +use Doctrine\ORM\Query\AST\InputParameter; +use Doctrine\ORM\Query\AST\ConditionalPrimary; +use Doctrine\ORM\Query\AST\ConditionalTerm; +use Doctrine\ORM\Query\AST\ConditionalExpression; +use Doctrine\ORM\Query\AST\ConditionalFactor; +use 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 + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + + if (count($from) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($rootClass->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName); + $pathExpression->type = $pathType; + + $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); + $expression->literals[] = new InputParameter(":" . self::PAGINATOR_ID_ALIAS); + + } 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 + || $AST->whereClause->conditionalExpression instanceof ConditionalFactor + ) { + $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 00000000000..574c9c28e12 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -0,0 +1,144 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\EventSubscriber; +use Doctrine\ORM\Events; + +/** + * ResolveTargetEntityListener + * + * Mechanism to overwrite interfaces or classes specified as association + * targets. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class ResolveTargetEntityListener implements EventSubscriber +{ + /** + * @var array[] indexed by original entity name + */ + private $resolveTargetEntities = array(); + + /** + * {@inheritDoc} + */ + public function getSubscribedEvents() + { + return array( + Events::loadClassMetadata, + Events::onClassMetadataNotFound + ); + } + + /** + * Adds 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; + } + + /** + * @param OnClassMetadataNotFoundEventArgs $args + * + * @internal this is an event callback, and should not be called directly + * + * @return void + */ + public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args) + { + if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) { + $args->setFoundMetadata( + $args + ->getObjectManager() + ->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity']) + ); + } + } + + /** + * Processes event and resolves new target entity names. + * + * @param LoadClassMetadataEventArgs $args + * + * @return void + * + * @internal this is an event callback, and should not be called directly + */ + public function loadClassMetadata(LoadClassMetadataEventArgs $args) + { + /* @var $cm \Doctrine\ORM\Mapping\ClassMetadata */ + $cm = $args->getClassMetadata(); + + foreach ($cm->associationMappings as $mapping) { + if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) { + $this->remapAssociation($cm, $mapping); + } + } + + foreach ($this->resolveTargetEntities as $interface => $data) { + if ($data['targetEntity'] == $cm->getName()) { + $args->getEntityManager()->getMetadataFactory()->setMetadataFor($interface, $cm); + } + } + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata + * @param array $mapping + * + * @return void + */ + 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 00000000000..b8e62739bd0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -0,0 +1,874 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs; +use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; + +/** + * The SchemaTool is a tool to create/drop/update database schemas based on + * ClassMetadata class descriptors. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Stefano Rodriguez + */ +class SchemaTool +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * Initializes a new SchemaTool instance that uses the connection of the + * provided EntityManager. + * + * @param \Doctrine\ORM\EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->platform = $em->getConnection()->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Creates the database schema for the given array of ClassMetadata instances. + * + * @param array $classes + * + * @return void + * + * @throws ToolsException + */ + 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 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); + } + + /** + * Detects instances of ClassMetadata that don't need to be processed in the SchemaTool context. + * + * @param ClassMetadata $class + * @param array $processedClasses + * + * @return bool + */ + private function processingNotRequired($class, array $processedClasses) + { + return ( + isset($processedClasses[$class->name]) || + $class->isMappedSuperclass || + $class->isEmbeddedClass || + ($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName) + ); + } + + /** + * Creates a Schema instance from a given set of metadata classes. + * + * @param array $classes + * + * @return Schema + * + * @throws \Doctrine\ORM\ORMException + */ + public function getSchemaFromMetadata(array $classes) + { + // Reminder for processed classes, used for hierarchies + $processedClasses = array(); + $eventManager = $this->em->getEventManager(); + $schemaManager = $this->em->getConnection()->getSchemaManager(); + $metadataSchemaConfig = $schemaManager->createSchemaConfig(); + + $metadataSchemaConfig->setExplicitForeignKeyIndexes(false); + $schema = new Schema(array(), array(), $metadataSchemaConfig); + + $addedFks = array(); + $blacklistedFks = array(); + + foreach ($classes as $class) { + /** @var \Doctrine\ORM\Mapping\ClassMetadata $class */ + if ($this->processingNotRequired($class, $processedClasses)) { + continue; + } + + $table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform)); + + if ($class->isInheritanceTypeSingleTable()) { + $this->gatherColumns($class, $table); + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + + // 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, $addedFks, $blacklistedFks); + $processedClasses[$subClassName] = true; + } + } elseif ($class->isInheritanceTypeJoined()) { + // Add all non-inherited fields as columns + $pkColumns = array(); + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ( ! isset($mapping['inherited'])) { + $columnName = $this->quoteStrategy->getColumnName( + $mapping['fieldName'], + $class, + $this->platform + ); + $this->gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($fieldName)) { + $pkColumns[] = $columnName; + } + } + } + + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + + // 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 + $inheritedKeyColumns = array(); + foreach ($class->identifier as $identifierField) { + $idMapping = $class->fieldMappings[$identifierField]; + if (isset($idMapping['inherited'])) { + $this->gatherColumn($class, $idMapping, $table); + $columnName = $this->quoteStrategy->getColumnName( + $identifierField, + $class, + $this->platform + ); + // TODO: This seems rather hackish, can we optimize it? + $table->getColumn($columnName)->setAutoincrement(false); + + $pkColumns[] = $columnName; + $inheritedKeyColumns[] = $columnName; + } + } + if (!empty($inheritedKeyColumns)) { + // Add a FK constraint on the ID column + $table->addForeignKeyConstraint( + $this->quoteStrategy->getTableName( + $this->em->getClassMetadata($class->rootEntityName), + $this->platform + ), + $inheritedKeyColumns, + $inheritedKeyColumns, + array('onDelete' => 'CASCADE') + ); + } + + } + + $table->setPrimaryKey($pkColumns); + + } elseif ($class->isInheritanceTypeTablePerClass()) { + throw ORMException::notSupported(); + } else { + $this->gatherColumns($class, $table); + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + } + + $pkColumns = array(); + foreach ($class->identifier as $identifierField) { + if (isset($class->fieldMappings[$identifierField])) { + $pkColumns[] = $this->quoteStrategy->getColumnName($identifierField, $class, $this->platform); + } elseif (isset($class->associationMappings[$identifierField])) { + /* @var $assoc \Doctrine\ORM\Mapping\OneToOne */ + $assoc = $class->associationMappings[$identifierField]; + foreach ($assoc['joinColumns'] as $joinColumn) { + $pkColumns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + } + } + + if ( ! $table->hasIndex('primary')) { + $table->setPrimaryKey($pkColumns); + } + + if (isset($class->table['indexes'])) { + foreach ($class->table['indexes'] as $indexName => $indexData) { + if( ! isset($indexData['flags'])) { + $indexData['flags'] = array(); + } + + $table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName, (array)$indexData['flags'], isset($indexData['options']) ? $indexData['options'] : array()); + } + } + + if (isset($class->table['uniqueConstraints'])) { + foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) { + $uniqIndex = new Index($indexName, $indexData['columns'], true, false, [], isset($indexData['options']) ? $indexData['options'] : []); + + foreach ($table->getIndexes() as $tableIndexName => $tableIndex) { + if ($tableIndex->isFullfilledBy($uniqIndex)) { + $table->dropIndex($tableIndexName); + break; + } + } + + $table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName, isset($indexData['options']) ? $indexData['options'] : array()); + } + } + + if (isset($class->table['options'])) { + foreach ($class->table['options'] as $key => $val) { + $table->addOption($key, $val); + } + } + + $processedClasses[$class->name] = true; + + if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) { + $seqDef = $class->sequenceGeneratorDefinition; + $quotedName = $this->quoteStrategy->getSequenceName($seqDef, $class, $this->platform); + if ( ! $schema->hasSequence($quotedName)) { + $schema->createSequence( + $quotedName, + $seqDef['allocationSize'], + $seqDef['initialValue'] + ); + } + } + + if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) { + $eventManager->dispatchEvent( + ToolEvents::postGenerateSchemaTable, + new GenerateSchemaTableEventArgs($class, $schema, $table) + ); + } + } + + if ( ! $this->platform->supportsSchemas() && ! $this->platform->canEmulateSchemas() ) { + $schema->visit(new RemoveNamespacedAssets()); + } + + if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) { + $eventManager->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 + * @param Table $table + * + * @return array The portable column definition of the discriminator column as required by + * the DBAL. + */ + private function addDiscriminatorColumnDefinition($class, Table $table) + { + $discrColumn = $class->discriminatorColumn; + + if ( ! isset($discrColumn['type']) || + (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null) + ) { + $discrColumn['type'] = 'string'; + $discrColumn['length'] = 255; + } + + $options = array( + 'length' => isset($discrColumn['length']) ? $discrColumn['length'] : null, + 'notnull' => true + ); + + if (isset($discrColumn['columnDefinition'])) { + $options['columnDefinition'] = $discrColumn['columnDefinition']; + } + + $table->addColumn($discrColumn['name'], $discrColumn['type'], $options); + } + + /** + * 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 $table) + { + $pkColumns = array(); + + foreach ($class->fieldMappings as $mapping) { + if ($class->isInheritanceTypeSingleTable() && isset($mapping['inherited'])) { + continue; + } + + $this->gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($mapping['fieldName'])) { + $pkColumns[] = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $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); + } + } + + /** + * 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 $table) + { + $columnName = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $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'])) { + $knownOptions = array('comment', 'unsigned', 'fixed', 'default'); + + foreach ($knownOptions as $knownOption) { + if (array_key_exists($knownOption, $mapping['options'])) { + $options[$knownOption] = $mapping['options'][$knownOption]; + + unset($mapping['options'][$knownOption]); + } + } + + $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 Table $table + * @param Schema $schema + * @param array $addedFks + * @param array $blacklistedFks + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + */ + private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$blacklistedFks) + { + foreach ($class->associationMappings as $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $foreignClass = $this->em->getClassMetadata($mapping['targetEntity']); + + if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { + $primaryKeyColumns = array(); // PK is unnecessary for this relation-type + + $this->gatherRelationJoinColumns( + $mapping['joinColumns'], + $table, + $foreignClass, + $mapping, + $primaryKeyColumns, + $addedFks, + $blacklistedFks + ); + } elseif ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { + //... create join table, one-many through join table supported later + throw ORMException::notSupported(); + } elseif ($mapping['type'] == ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) { + // create join table + $joinTable = $mapping['joinTable']; + + $theJoinTable = $schema->createTable( + $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform) + ); + + $primaryKeyColumns = array(); + + // Build first FK constraint (relation table => source table) + $this->gatherRelationJoinColumns( + $joinTable['joinColumns'], + $theJoinTable, + $class, + $mapping, + $primaryKeyColumns, + $addedFks, + $blacklistedFks + ); + + // Build second FK constraint (relation table => target table) + $this->gatherRelationJoinColumns( + $joinTable['inverseJoinColumns'], + $theJoinTable, + $foreignClass, + $mapping, + $primaryKeyColumns, + $addedFks, + $blacklistedFks + ); + + $theJoinTable->setPrimaryKey($primaryKeyColumns); + } + } + } + + /** + * Gets 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 recursively 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); + } + + 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; + } + + /** + * Gathers columns and fk constraints that are required for one part of relationship. + * + * @param array $joinColumns + * @param Table $theJoinTable + * @param ClassMetadata $class + * @param array $mapping + * @param array $primaryKeyColumns + * @param array $addedFks + * @param array $blacklistedFks + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + */ + private function gatherRelationJoinColumns( + $joinColumns, + $theJoinTable, + $class, + $mapping, + &$primaryKeyColumns, + &$addedFks, + &$blacklistedFks + ) { + $localColumns = array(); + $foreignColumns = array(); + $fkOptions = array(); + $foreignTableName = $this->quoteStrategy->getTableName($class, $this->platform); + $uniqueConstraints = array(); + + foreach ($joinColumns as $joinColumn) { + + 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." + ); + } + + $quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName( + $joinColumn, + $class, + $this->platform + ); + + $primaryKeyColumns[] = $quotedColumnName; + $localColumns[] = $quotedColumnName; + $foreignColumns[] = $quotedRefColumnName; + + if ( ! $theJoinTable->hasColumn($quotedColumnName)) { + // 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']; + } elseif (isset($fieldMapping['columnDefinition'])) { + $columnDef = $fieldMapping['columnDefinition']; + } + + $columnOptions = array('notnull' => false, 'columnDefinition' => $columnDef); + + if (isset($joinColumn['nullable'])) { + $columnOptions['notnull'] = !$joinColumn['nullable']; + } + + if (isset($fieldMapping['options'])) { + $columnOptions['options'] = $fieldMapping['options']; + } + + if ($fieldMapping['type'] == "string" && isset($fieldMapping['length'])) { + $columnOptions['length'] = $fieldMapping['length']; + } elseif ($fieldMapping['type'] == "decimal") { + $columnOptions['scale'] = $fieldMapping['scale']; + $columnOptions['precision'] = $fieldMapping['precision']; + } + + $theJoinTable->addColumn($quotedColumnName, $fieldMapping['type'], $columnOptions); + } + + if (isset($joinColumn['unique']) && $joinColumn['unique'] == true) { + $uniqueConstraints[] = array('columns' => array($quotedColumnName)); + } + + if (isset($joinColumn['onDelete'])) { + $fkOptions['onDelete'] = $joinColumn['onDelete']; + } + } + + // Prefer unique constraints over implicit simple indexes created for foreign keys. + // Also avoids index duplication. + foreach ($uniqueConstraints as $indexName => $unique) { + $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + + $compositeName = $theJoinTable->getName().'.'.implode('', $localColumns); + if (isset($addedFks[$compositeName]) + && ($foreignTableName != $addedFks[$compositeName]['foreignTableName'] + || 0 < count(array_diff($foreignColumns, $addedFks[$compositeName]['foreignColumns']))) + ) { + foreach ($theJoinTable->getForeignKeys() as $fkName => $key) { + if (0 === count(array_diff($key->getLocalColumns(), $localColumns)) + && (($key->getForeignTableName() != $foreignTableName) + || 0 < count(array_diff($key->getForeignColumns(), $foreignColumns))) + ) { + $theJoinTable->removeForeignKey($fkName); + break; + } + } + $blacklistedFks[$compositeName] = true; + } elseif (!isset($blacklistedFks[$compositeName])) { + $addedFks[$compositeName] = array('foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns); + $theJoinTable->addUnnamedForeignKeyConstraint( + $foreignTableName, + $localColumns, + $foreignColumns, + $fkOptions + ); + } + } + + /** + * Drops the database schema for the given classes. + * + * In any way when an exception is thrown it is suppressed 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 DropSchemaSqlCollector($this->platform); + $schema->visit($visitor); + + return $visitor->getQueries(); + } + + /** + * Gets SQL to drop the tables defined by the passed classes. + * + * @param array $classes + * + * @return array + */ + public function getDropSchemaSQL(array $classes) + { + $visitor = new 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 Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($saveMode) { + return $schemaDiff->toSaveSql($this->platform); + } + + 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 00000000000..8f59ea72dfb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -0,0 +1,272 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\DBAL\Types\Type; + +/** + * Performs strict validation of the mapping schema + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SchemaValidator +{ + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $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. + * + * @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; + } + + /** + * Validates 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."; + } + + 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, but as field."; + } + 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."; + } elseif ($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."; + } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ". + "inconsistent 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."; + } elseif ($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."; + } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ". + "inconsistent 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."; + } elseif ($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."; + } elseif ($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."; + } + + } elseif ($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 '". $targetMetadata->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) && !$targetMetadata->hasAssociation($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 . "."; + continue; + } + if ($targetMetadata->isCollectionValuedAssociation($orderField)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a field " . + $orderField . " on " . $targetMetadata->name . " that is a collection-valued association."; + continue; + } + if ($targetMetadata->isAssociationInverseSide($orderField)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a field " . + $orderField . " on " . $targetMetadata->name . " that is the inverse side of an association."; + continue; + } + } + } + } + + 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; + } + + /** + * Checks 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 00000000000..79d4670ad0f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php @@ -0,0 +1,162 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\ClassLoader; +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\CacheProvider; +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 autoloads for a downloaded Doctrine library. + * Pick the directory the library was uncompressed into. + * + * @param string $directory + * + * @return void + */ + public static 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(); + } + + /** + * Creates a configuration with an annotation metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @param bool $useSimpleAnnotationReader + * + * @return Configuration + */ + public static function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null, $useSimpleAnnotationReader = true) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths, $useSimpleAnnotationReader)); + + return $config; + } + + /** + * Creates a configuration with a xml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new XmlDriver($paths)); + + return $config; + } + + /** + * Creates a configuration with a yaml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new YamlDriver($paths)); + + return $config; + } + + /** + * Creates a configuration without a metadata driver. + * + * @param bool $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static 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(); + } elseif (extension_loaded('xcache')) { + $cache = new \Doctrine\Common\Cache\XcacheCache(); + } elseif (extension_loaded('memcache')) { + $memcache = new \Memcache(); + $memcache->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $cache->setMemcache($memcache); + } elseif (extension_loaded('redis')) { + $redis = new \Redis(); + $redis->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\RedisCache(); + $cache->setRedis($redis); + } else { + $cache = new ArrayCache(); + } + } elseif ($cache === null) { + $cache = new ArrayCache(); + } + + if ($cache instanceof CacheProvider) { + $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; + } +} 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 00000000000..aebb5d8f37d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +class ToolEvents +{ + /** + * The postGenerateSchemaTable event occurs in SchemaTool#getSchemaFromMetadata() + * whenever an entity class is transformed into its table representation. It receives + * 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'; +} 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 00000000000..0a461640491 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; + +/** + * Tools related Exceptions. + * + * @author Benjamin Eberlei + */ +class ToolsException extends ORMException +{ + /** + * @param string $sql + * @param \Exception $e + * + * @return ToolsException + */ + public static function schemaToolFailure($sql, \Exception $e) + { + return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e); + } + + /** + * @param string $type + * + * @return ToolsException + */ + public static function couldNotMapDoctrine1Type($type) + { + return new self("Could not map doctrine 1 type '$type'!"); + } +} 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 00000000000..c3417b67426 --- /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/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class TransactionRequiredException extends ORMException +{ + /** + * @return TransactionRequiredException + */ + static public function transactionRequired() + { + return new self('An open transaction is required for this operation.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php new file mode 100644 index 00000000000..3cd561f0965 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception for a unexpected query result. + * + * @author Fabio B. Silva + * @since 2.3 + */ +class UnexpectedResultException extends ORMException +{ +} 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 00000000000..d27748a99a7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php @@ -0,0 +1,3474 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Persistence\Mapping\RuntimeReflectionService; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Internal\HydrationCompleteHandler; +use Doctrine\ORM\Mapping\Reflection\ReflectionPropertiesGetter; +use Exception; +use InvalidArgumentException; +use UnexpectedValueException; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\NotifyPropertyChanged; +use Doctrine\Common\PropertyChangedListener; +use Doctrine\Common\Persistence\ObjectManagerAware; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Proxy\Proxy; + +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\PreUpdateEventArgs; +use Doctrine\ORM\Event\PreFlushEventArgs; +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Event\PostFlushEventArgs; +use Doctrine\ORM\Event\ListenersInvoker; + +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; +use Doctrine\ORM\Persisters\Entity\SingleTablePersister; +use Doctrine\ORM\Persisters\Entity\JoinedSubclassPersister; +use Doctrine\ORM\Persisters\Collection\OneToManyPersister; +use Doctrine\ORM\Persisters\Collection\ManyToManyPersister; +use Doctrine\ORM\Utility\IdentifierFlattener; +use Doctrine\ORM\Cache\AssociationCacheEntry; + +/** + * 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. + * + * Internal note: This class contains highly performance-sensitive code. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Rob Caiger + */ +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; + + /** + * Hint used to collect all primary keys of associated entities during hydration + * and execute it in a dedicated query afterwards + * @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql + */ + const HINT_DEFEREAGERLOAD = 'deferEagerLoad'; + + /** + * 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. + * + * Internal note: 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. + * + * @var array + */ + 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 + */ + private $scheduledForSynchronization = 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 EntityManagerInterface + */ + 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 \Doctrine\Common\EventManager + */ + private $evm; + + /** + * The ListenersInvoker used for dispatching events. + * + * @var \Doctrine\ORM\Event\ListenersInvoker + */ + private $listenersInvoker; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * 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(); + + /** + * @var boolean + */ + protected $hasCache = false; + + /** + * Helper for handling completion of hydration + * + * @var HydrationCompleteHandler + */ + private $hydrationCompleteHandler; + + /** + * @var ReflectionPropertiesGetter + */ + private $reflectionPropertiesGetter; + + /** + * Initializes a new UnitOfWork instance, bound to the given EntityManager. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->evm = $em->getEventManager(); + $this->listenersInvoker = new ListenersInvoker($em); + $this->hasCache = $em->getConfiguration()->isSecondLevelCacheEnabled(); + $this->identifierFlattener = new IdentifierFlattener($this, $em->getMetadataFactory()); + $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em); + $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService()); + } + + /** + * 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 null|object|array $entity + * + * @return void + * + * @throws \Exception + */ + public function commit($entity = null) + { + // Raise preFlush + if ($this->evm->hasListeners(Events::preFlush)) { + $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em)); + } + + // Compute changes done since last commit. + if ($entity === null) { + $this->computeChangeSets(); + } elseif (is_object($entity)) { + $this->computeSingleEntityChangeSet($entity); + } elseif (is_array($entity)) { + foreach ($entity as $object) { + $this->computeSingleEntityChangeSet($object); + } + } + + if ( ! ($this->entityInsertions || + $this->entityDeletions || + $this->entityUpdates || + $this->collectionUpdates || + $this->collectionDeletions || + $this->orphanRemovals)) { + $this->dispatchOnFlushEvent(); + $this->dispatchPostFlushEvent(); + + return; // Nothing to do. + } + + if ($this->orphanRemovals) { + foreach ($this->orphanRemovals as $orphan) { + $this->remove($orphan); + } + } + + $this->dispatchOnFlushEvent(); + + // 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 && $this->entityDeletions; --$i) { + $this->executeDeletions($commitOrder[$i]); + } + } + + $conn->commit(); + } catch (Exception $e) { + $this->em->close(); + $conn->rollback(); + + $this->afterTransactionRolledBack(); + + throw $e; + } + + $this->afterTransactionComplete(); + + // Take new snapshots from visited collections + foreach ($this->visitedCollections as $coll) { + $coll->takeSnapshot(); + } + + $this->dispatchPostFlushEvent(); + + // Clear up + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->extraUpdates = + $this->entityChangeSets = + $this->collectionUpdates = + $this->collectionDeletions = + $this->visitedCollections = + $this->scheduledForSynchronization = + $this->orphanRemovals = array(); + } + + /** + * Computes 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 flushes 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 + * + * @throws \InvalidArgumentException + */ + private function computeSingleEntityChangeSet($entity) + { + $state = $this->getEntityState($entity); + + if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) { + throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity)); + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($state === self::STATE_MANAGED && $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 OR DELETION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$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); + } + + $this->extraUpdates = array(); + } + + /** + * Gets the changeset for an entity. + * + * @param object $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. + * + * @return void + */ + 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)); + } + + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER; + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + $value = $refProp->getValue($entity); + + if ($class->isCollectionValuedAssociation($name) && $value !== null) { + if ($value instanceof PersistentCollection) { + if ($value->getOwner() === $entity) { + continue; + } + + $value = new ArrayCollection($value->getValues()); + } + + // 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 haven't 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) { + continue; + } + + $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. + * + * @return void + */ + 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->scheduledForSynchronization[$className])): + $entitiesToProcess = $this->scheduledForSynchronization[$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 OR DELETION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + } + } + + /** + * Computes the changes of an association. + * + * @param array $assoc The association mapping. + * @param mixed $value The value of the association. + * + * @throws ORMInvalidArgumentException + * @throws ORMException + * + * @return void + */ + 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) { + if (! ($entry instanceof $targetClass->name)) { + throw ORMInvalidArgumentException::invalidAssociation($targetClass, $assoc, $entry); + } + + $state = $this->getEntityState($entry, self::STATE_NEW); + + if ( ! ($entry instanceof $assoc['targetEntity'])) { + throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']); + } + + 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. + } + } + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * @param object $entity + * + * @return void + */ + private function persistNew($class, $entity) + { + $oid = spl_object_hash($entity); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + + $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. + * + * @return void + * + * @throws ORMInvalidArgumentException 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()) + && ($name !== $class->versionField) + && ! $class->isCollectionValuedAssociation($name)) { + $actualData[$name] = $refProp->getValue($entity); + } + } + + if ( ! isset($this->originalEntityData[$oid])) { + throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.'); + } + + $originalData = $this->originalEntityData[$oid]; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; + + if ($orgValue !== $actualValue) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + } + + if ($changeSet) { + if (isset($this->entityChangeSets[$oid])) { + $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet); + } else if ( ! isset($this->entityInsertions[$oid])) { + $this->entityChangeSets[$oid] = $changeSet; + $this->entityUpdates[$oid] = $entity; + } + $this->originalEntityData[$oid] = $actualData; + } + } + + /** + * Executes all entity insertions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeInserts($class) + { + $entities = array(); + $className = $class->name; + $persister = $this->getEntityPersister($className); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist); + + foreach ($this->entityInsertions as $oid => $entity) { + + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + $persister->addInsert($entity); + + unset($this->entityInsertions[$oid]); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $entities[] = $entity; + } + } + + $postInsertIds = $persister->executeInserts(); + + if ($postInsertIds) { + // Persister returned post-insert IDs + foreach ($postInsertIds as $postInsertId) { + $id = $postInsertId['generatedId']; + $entity = $postInsertId['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) { + $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + } + + /** + * Executes all entity updates for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeUpdates($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $preUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate); + $postUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate); + + foreach ($this->entityUpdates as $oid => $entity) { + + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]), $preUpdateInvoke); + $this->recomputeSingleEntityChangeSet($class, $entity); + } + + if ( ! empty($this->entityChangeSets[$oid])) { + $persister->update($entity); + } + + unset($this->entityUpdates[$oid]); + + if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke); + } + } + } + + /** + * Executes all entity deletions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeDeletions($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove); + + foreach ($this->entityDeletions as $oid => $entity) { + if ($this->em->getClassMetadata(get_class($entity))->name !== $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 ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + } + } + + /** + * Gets the commit order. + * + * @param array|null $entityChangeSet + * + * @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 (don't 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 $entity) { + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($calc->hasClass($class->name)) { + continue; + } + + $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. + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws \InvalidArgumentException + */ + 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); + } + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + } + + /** + * 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. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + 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). + * + * @return void + */ + 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->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]); + } + + /** + * INTERNAL: + * Schedules an entity for deletion. + * + * @param object $entity + * + * @return void + */ + 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 object $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. + * + * @throws ORMInvalidArgumentException + */ + 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; + + return true; + } + + /** + * Gets the state of an entity with regard to the current unit of work. + * + * @param object $entity + * @param int|null $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; + } + + if ($class->containsForeignIdentifier) { + $id = $this->identifierFlattener->flattenIdentifier($class, $id); + } + + 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($class->name)->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($class->name)->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 + * + * @throws ORMInvalidArgumentException + */ + 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 mixed $idHash (must be possible to cast it to string) + * @param string $rootClassName + * + * @return object|bool The found entity or FALSE. + */ + public function tryGetByIdHash($idHash, $rootClassName) + { + $stringIdHash = (string) $idHash; + + if (isset($this->identityMap[$rootClassName][$stringIdHash])) { + return $this->identityMap[$rootClassName][$stringIdHash]; + } + + 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. + * + * @return void + */ + 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. + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws UnexpectedValueException + */ + 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->addToIdentityMap($entity); + + $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. + * + * @return void + */ + 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. + * + * @return void + * + * @throws ORMInvalidArgumentException If the instance is a detached entity. + * @throws UnexpectedValueException + */ + 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: + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + + $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 + * @param object|null $prevManagedCopy + * @param array|null $assoc + * + * @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 ORMInvalidArgumentException If the entity instance is NEW. + * @throws EntityNotFoundException + */ + private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + $managedCopy = $visited[$oid]; + + if ($prevManagedCopy !== null) { + $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); + } + + return $managedCopy; + } + + $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) { + // 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 = ($class->containsForeignIdentifier) + ? $this->identifierFlattener->flattenIdentifier($class, $id) + : $id; + + $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 EntityNotFoundException::fromClassNameAndIdentifier( + $class->getName(), + $this->identifierFlattener->flattenIdentifier($class, $id) + ); + } + + $managedCopy = $this->newInstance($class); + $class->setIdentifierValues($managedCopy, $id); + + $this->persistNew($class, $managedCopy); + } + } + + if ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity)) { + $reflField = $class->reflFields[$class->versionField]; + $managedCopyVersion = $reflField->getValue($managedCopy); + $entityVersion = $reflField->getValue($entity); + + // Throw exception if versions don't match. + if ($managedCopyVersion != $entityVersion) { + throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion); + } + } + + $visited[$oid] = $managedCopy; // mark visited + + if ($this->isLoaded($entity)) { + if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) { + $managedCopy->__load(); + } + + $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); + } + + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + } + + if ($prevManagedCopy !== null) { + $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); + } + + // Mark the managed copy visited as well + $visited[spl_object_hash($managedCopy)] = $managedCopy; + + $this->cascadeMerge($entity, $managedCopy, $visited); + + return $managedCopy; + } + + /** + * Tests if an entity is loaded - must either be a loaded proxy or not a proxy + * + * @param object $entity + * + * @return bool + */ + private function isLoaded($entity) + { + return !($entity instanceof Proxy) || $entity->__isInitialized(); + } + + /** + * Sets/adds associated managed copies into the previous entity's association field + * + * @param object $entity + * @param array $association + * @param object $previousManagedCopy + * @param object $managedCopy + * + * @return void + */ + private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy) + { + $assocField = $association['fieldName']; + $prevClass = $this->em->getClassMetadata(get_class($previousManagedCopy)); + + if ($association['type'] & ClassMetadata::TO_ONE) { + $prevClass->reflFields[$assocField]->setValue($previousManagedCopy, $managedCopy); + + return; + } + + $value = $prevClass->reflFields[$assocField]->getValue($previousManagedCopy); + $value[] = $managedCopy; + + if ($association['type'] == ClassMetadata::ONE_TO_MANY) { + $class = $this->em->getClassMetadata(get_class($entity)); + + $class->reflFields[$association['mappedBy']]->setValue($managedCopy, $previousManagedCopy); + } + } + + /** + * Detaches an entity from the persistence management. It's persistence will + * no longer be managed by Doctrine. + * + * @param object $entity The entity to detach. + * + * @return void + */ + 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. + * + * @return void + */ + 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. + * + * @return void + * + * @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. + * + * @return void + * + * @throws ORMInvalidArgumentException 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 + * + * @return void + */ + 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 + * + * @return void + */ + 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 + * + * @return void + */ + 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 + * + * @return void + */ + 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)): + if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) { + throw ORMInvalidArgumentException::invalidAssociation( + $this->em->getClassMetadata($assoc['targetEntity']), + $assoc, + $relatedEntities + ); + } + + foreach ($relatedEntities as $relatedEntity) { + $this->doPersist($relatedEntity, $visited); + } + + break; + + case ($relatedEntities !== null): + if (! $relatedEntities instanceof $assoc['targetEntity']) { + throw ORMInvalidArgumentException::invalidAssociation( + $this->em->getClassMetadata($assoc['targetEntity']), + $assoc, + $relatedEntities + ); + } + + $this->doPersist($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades the delete operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadeRemove($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRemove']; } + ); + + $entitiesToCascade = array(); + + 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) { + $entitiesToCascade[] = $relatedEntity; + } + break; + + case ($relatedEntities !== null): + $entitiesToCascade[] = $relatedEntities; + break; + + default: + // Do nothing + } + } + + foreach ($entitiesToCascade as $relatedEntity) { + $this->doRemove($relatedEntity, $visited); + } + } + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int $lockVersion + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws OptimisticLockException + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + if ($entity === null) { + throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock()."); + } + + if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + switch (true) { + case LockMode::OPTIMISTIC === $lockMode: + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($class->name); + } + + if ($lockVersion === null) { + return; + } + + if ($entity instanceof Proxy && !$entity->__isInitialized__) { + $entity->__load(); + } + + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); + + if ($entityVersion != $lockVersion) { + throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion); + } + + break; + + case LockMode::NONE === $lockMode: + case LockMode::PESSIMISTIC_READ === $lockMode: + case LockMode::PESSIMISTIC_WRITE === $lockMode: + 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|null $entityName if given, only entities of this type will get detached. + * + * @return void + */ + public function clear($entityName = null) + { + if ($entityName === null) { + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForSynchronization = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->readOnlyObjects = + $this->visitedCollections = + $this->orphanRemovals = array(); + + if ($this->commitOrderCalculator !== null) { + $this->commitOrderCalculator->clear(); + } + } else { + $visited = array(); + + foreach ($this->identityMap as $className => $entities) { + if ($className !== $entityName) { + continue; + } + + foreach ($entities as $entity) { + $this->doDetach($entity, $visited, false); + } + } + } + + 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 + * + * @return void + */ + 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 + * + * @return void + */ + 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; + } + + /** + * @param PersistentCollection $coll + * + * @return bool + */ + public function isCollectionScheduledForDeletion(PersistentCollection $coll) + { + return isset($this->collectionDeletions[spl_object_hash($coll)]); + } + + /** + * @param ClassMetadata $class + * + * @return \Doctrine\Common\Persistence\ObjectManagerAware|object + */ + 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. + * + * Internal note: Highly performance-sensitive method. + * + * @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. + * + * @todo Rename: getOrCreateEntity + */ + public function createEntity($className, array $data, &$hints = array()) + { + $class = $this->em->getClassMetadata($className); + //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]); + + $id = $this->identifierFlattener->flattenIdentifier($class, $data); + $idHash = implode(' ', $id); + + if (isset($this->identityMap[$class->rootEntityName][$idHash])) { + $entity = $this->identityMap[$class->rootEntityName][$idHash]; + $oid = spl_object_hash($entity); + + if ( + isset($hints[Query::HINT_REFRESH]) + && isset($hints[Query::HINT_REFRESH_ENTITY]) + && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity + && $unmanagedProxy instanceof Proxy + && $this->isIdentifierEquals($unmanagedProxy, $entity) + ) { + // DDC-1238 - we have a managed instance, but it isn't the provided one. + // Therefore we clear its identifier. Also, we must re-fetch metadata since the + // refreshed object may be anything + + foreach ($class->identifier as $fieldName) { + $class->reflFields[$fieldName]->setValue($unmanagedProxy, null); + } + + return $unmanagedProxy; + } + + if ($entity instanceof Proxy && ! $entity->__isInitialized()) { + $entity->__setInitialized(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; + } + } + + if ($overrideLocalValues) { + // inject ObjectManager upon refresh. + if ($entity instanceof ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + + $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']) { + + // use the given entity association + if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) { + + $this->originalEntityData[$oid][$field] = $data[$field]; + + $class->reflFields[$field]->setValue($entity, $data[$field]); + $targetClass->reflFields[$assoc['mappedBy']]->setValue($data[$field], $entity); + + continue 2; + } + + // Inverse side of x-to-one can never be lazy + $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity)); + + continue 2; + } + + // use the entity association + if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) { + $class->reflFields[$field]->setValue($entity, $data[$field]); + $this->originalEntityData[$oid][$field] = $data[$field]; + + continue; + } + + $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; + } + } elseif ($targetClass->containsForeignIdentifier + && in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true) + ) { + // the missing key is part of target's entity primary key + $associatedId = array(); + break; + } + } + + 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 can append this entity for eager loading! + if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER && + isset($hints[self::HINT_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[self::HINT_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; + + if ( + $newValue instanceof NotifyPropertyChanged && + ( ! $newValue instanceof Proxy || $newValue->__isInitialized()) + ) { + $newValue->addPropertyChangedListener($this); + } + $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: + // Ignore if its a cached collection + if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) { + break; + } + + // use the given collection + if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) { + + $data[$field]->setOwner($entity, $assoc); + + $class->reflFields[$field]->setValue($entity, $data[$field]); + $this->originalEntityData[$oid][$field] = $data[$field]; + + break; + } + + // 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) { + // defer invoking of postLoad event to hydration complete step + $this->hydrationCompleteHandler->deferPostLoadInvoking($class, $entity); + } + + 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) { + if ( ! $ids) { + continue; + } + + $class = $this->em->getClassMetadata($entityName); + + $this->getEntityPersister($entityName)->loadAll( + array_combine($class->identifier, array(array_values($ids))) + ); + } + } + + /** + * Initializes (loads) an uninitialized persistent collection of an entity. + * + * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize. + * + * @return void + * + * @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; + } + + $collection->setInitialized(true); + } + + /** + * 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 + * + * @param object $entity + * @param array $data + * + * @return void + */ + 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 + * + * @return void + */ + 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)]; + } + + /** + * Processes an entity instance to extract their identifier values. + * + * @param object $entity The entity instance. + * + * @return mixed A scalar value. + * + * @throws \Doctrine\ORM\ORMInvalidArgumentException + */ + public function getSingleIdentifierValue($entity) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($class->isIdentifierComposite) { + throw ORMInvalidArgumentException::invalidCompositeIdentifier(); + } + + $values = $this->isInIdentityMap($entity) + ? $this->getEntityIdentifier($entity) + : $class->getIdentifierValues($entity); + + return isset($values[$class->identifier[0]]) ? $values[$class->identifier[0]] : null; + } + + /** + * 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 object|bool 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. + * + * @return void + * + * @todo Rename: scheduleForSynchronization + */ + public function scheduleForDirtyCheck($entity) + { + $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + $this->scheduledForSynchronization[$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\Entity\EntityPersister + */ + 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 BasicEntityPersister($this->em, $class); + break; + + case ($class->isInheritanceTypeSingleTable()): + $persister = new SingleTablePersister($this->em, $class); + break; + + case ($class->isInheritanceTypeJoined()): + $persister = new JoinedSubclassPersister($this->em, $class); + break; + + default: + throw new \RuntimeException('No persister found for entity.'); + } + + if ($this->hasCache && $class->cache !== null) { + $persister = $this->em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheFactory() + ->buildCachedEntityPersister($this->em, $persister, $class); + } + + $this->persisters[$entityName] = $persister; + + return $this->persisters[$entityName]; + } + + /** + * Gets a collection persister for a collection-valued association. + * + * @param array $association + * + * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister + */ + public function getCollectionPersister(array $association) + { + $role = isset($association['cache']) + ? $association['sourceEntity'] . '::' . $association['fieldName'] + : $association['type']; + + if (isset($this->collectionPersisters[$role])) { + return $this->collectionPersisters[$role]; + } + + $persister = ClassMetadata::ONE_TO_MANY === $association['type'] + ? new OneToManyPersister($this->em) + : new ManyToManyPersister($this->em); + + if ($this->hasCache && isset($association['cache'])) { + $persister = $this->em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheFactory() + ->buildCachedCollectionPersister($this->em, $persister, $association); + } + + $this->collectionPersisters[$role] = $persister; + + return $this->collectionPersisters[$role]; + } + + /** + * INTERNAL: + * Registers an entity as managed. + * + * @param object $entity The entity. + * @param array $id The identifier values. + * @param array $data The original entity data. + * + * @return void + */ + 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); + + if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) { + $entity->addPropertyChangedListener($this); + } + } + + /** + * INTERNAL: + * Clears the property changeset of the entity with the given OID. + * + * @param string $oid The entity's OID. + * + * @return void + */ + 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. + * + * @return void + */ + 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->scheduledForSynchronization[$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; + } + + /** + * Gets 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 $obj + * + * @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 update. + * + * @param object $object + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + 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? + * + * @param object $object + * + * @return bool + * + * @throws ORMInvalidArgumentException + */ + public function isReadOnly($object) + { + if ( ! is_object($object)) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + return isset($this->readOnlyObjects[spl_object_hash($object)]); + } + + /** + * Perform whatever processing is encapsulated here after completion of the transaction. + */ + private function afterTransactionComplete() + { + if ( ! $this->hasCache) { + return; + } + + foreach ($this->persisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionComplete(); + } + } + + foreach ($this->collectionPersisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionComplete(); + } + } + } + + /** + * Perform whatever processing is encapsulated here after completion of the rolled-back. + */ + private function afterTransactionRolledBack() + { + if ( ! $this->hasCache) { + return; + } + + foreach ($this->persisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionRolledBack(); + } + } + + foreach ($this->collectionPersisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionRolledBack(); + } + } + } + + private function dispatchOnFlushEvent() + { + if ($this->evm->hasListeners(Events::onFlush)) { + $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em)); + } + } + + private function dispatchPostFlushEvent() + { + if ($this->evm->hasListeners(Events::postFlush)) { + $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em)); + } + } + + /** + * Verifies if two given entities actually are the same based on identifier comparison + * + * @param object $entity1 + * @param object $entity2 + * + * @return bool + */ + private function isIdentifierEquals($entity1, $entity2) + { + if ($entity1 === $entity2) { + return true; + } + + $class = $this->em->getClassMetadata(get_class($entity1)); + + if ($class !== $this->em->getClassMetadata(get_class($entity2))) { + return false; + } + + $oid1 = spl_object_hash($entity1); + $oid2 = spl_object_hash($entity2); + + $id1 = isset($this->entityIdentifiers[$oid1]) + ? $this->entityIdentifiers[$oid1] + : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity1)); + $id2 = isset($this->entityIdentifiers[$oid2]) + ? $this->entityIdentifiers[$oid2] + : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity2)); + + return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2); + } + + /** + * @param object $entity + * @param object $managedCopy + * + * @throws ORMException + * @throws OptimisticLockException + * @throws TransactionRequiredException + */ + private function mergeEntityStateIntoManagedCopy($entity, $managedCopy) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) { + $name = $prop->name; + + $prop->setAccessible(true); + + 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; + } + + if ( ! $assoc2['isCascadeMerge']) { + if ($this->getEntityState($other) === self::STATE_DETACHED) { + $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[spl_object_hash($entity)][$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)); + } + } + } + + /** + * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle. + * Unit of work able to fire deferred events, related to loading events here. + * + * @internal should be called internally from object hydrators + */ + public function hydrationComplete() + { + $this->hydrationCompleteHandler->hydrationComplete(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/IdentifierFlattener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/IdentifierFlattener.php new file mode 100644 index 00000000000..4a58dfeda1b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/IdentifierFlattener.php @@ -0,0 +1,103 @@ +. + */ + + +namespace Doctrine\ORM\Utility; + +use Doctrine\ORM\UnitOfWork; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; + +/** + * The IdentifierFlattener utility now houses some of the identifier manipulation logic from unit of work, so that it + * can be re-used elsewhere. + * + * @since 2.5 + * @author Rob Caiger + */ +final class IdentifierFlattener +{ + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var UnitOfWork + */ + private $unitOfWork; + + /** + * The metadata factory, used to retrieve the ORM metadata of entity classes. + * + * @var ClassMetadataFactory + */ + private $metadataFactory; + + /** + * Initializes a new IdentifierFlattener instance, bound to the given EntityManager. + * + * @param UnitOfWork $unitOfWork + * @param ClassMetadataFactory $metadataFactory + */ + public function __construct(UnitOfWork $unitOfWork, ClassMetadataFactory $metadataFactory) + { + $this->unitOfWork = $unitOfWork; + $this->metadataFactory = $metadataFactory; + } + + /** + * convert foreign identifiers into scalar foreign key values to avoid object to string conversion failures. + * + * @param ClassMetadata $class + * @param array $id + * + * @return array + */ + public function flattenIdentifier(ClassMetadata $class, array $id) + { + $flatId = array(); + + foreach ($class->identifier as $field) { + if (isset($class->associationMappings[$field]) && isset($id[$field]) && is_object($id[$field])) { + /* @var $targetClassMetadata ClassMetadata */ + $targetClassMetadata = $this->metadataFactory->getMetadataFor( + $class->associationMappings[$field]['targetEntity'] + ); + + if ($this->unitOfWork->isInIdentityMap($id[$field])) { + $associatedId = $this->flattenIdentifier($targetClassMetadata, $this->unitOfWork->getEntityIdentifier($id[$field])); + } else { + $associatedId = $this->flattenIdentifier($targetClassMetadata, $targetClassMetadata->getIdentifierValues($id[$field])); + } + + $flatId[$field] = implode(' ', $associatedId); + } elseif (isset($class->associationMappings[$field])) { + $associatedId = array(); + + foreach ($class->associationMappings[$field]['joinColumns'] as $joinColumn) { + $associatedId[] = $id[$joinColumn['name']]; + } + + $flatId[$field] = implode(' ', $associatedId); + } else { + $flatId[$field] = $id[$field]; + } + } + + return $flatId; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/PersisterHelper.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/PersisterHelper.php new file mode 100644 index 00000000000..6fe7545eab6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/PersisterHelper.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\ORM\Utility; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\QueryException; + +/** + * The PersisterHelper contains logic to infer binding types which is used in + * several persisters. + * + * @link www.doctrine-project.org + * @since 2.5 + * @author Jasper N. Brouwer + */ +class PersisterHelper +{ + /** + * @param string $fieldName + * @param ClassMetadata $class + * @param EntityManagerInterface $em + * + * @return array + * + * @throws QueryException + */ + public static function getTypeOfField($fieldName, ClassMetadata $class, EntityManagerInterface $em) + { + if (isset($class->fieldMappings[$fieldName])) { + return array($class->fieldMappings[$fieldName]['type']); + } + + if ( ! isset($class->associationMappings[$fieldName])) { + return array(); + } + + $assoc = $class->associationMappings[$fieldName]; + + if (! $assoc['isOwningSide']) { + return self::getTypeOfField($assoc['mappedBy'], $em->getClassMetadata($assoc['targetEntity']), $em); + } + + if ($assoc['type'] & ClassMetadata::MANY_TO_MANY) { + $joinData = $assoc['joinTable']; + } else { + $joinData = $assoc; + } + + $types = array(); + $targetClass = $em->getClassMetadata($assoc['targetEntity']); + + foreach ($joinData['joinColumns'] as $joinColumn) { + $types[] = self::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $em); + } + + return $types; + } + + /** + * @param string $columnName + * @param ClassMetadata $class + * @param EntityManagerInterface $em + * + * @return string + * + * @throws \RuntimeException + */ + public static function getTypeOfColumn($columnName, ClassMetadata $class, EntityManagerInterface $em) + { + if (isset($class->fieldNames[$columnName])) { + $fieldName = $class->fieldNames[$columnName]; + + if (isset($class->fieldMappings[$fieldName])) { + return $class->fieldMappings[$fieldName]['type']; + } + } + + // iterate over to-one association mappings + foreach ($class->associationMappings as $assoc) { + if ( ! isset($assoc['joinColumns'])) { + continue; + } + + foreach ($assoc['joinColumns'] as $joinColumn) { + if ($joinColumn['name'] == $columnName) { + $targetColumnName = $joinColumn['referencedColumnName']; + $targetClass = $em->getClassMetadata($assoc['targetEntity']); + + return self::getTypeOfColumn($targetColumnName, $targetClass, $em); + } + } + } + + // iterate over to-many association mappings + foreach ($class->associationMappings as $assoc) { + if ( ! (isset($assoc['joinTable']) && isset($assoc['joinTable']['joinColumns']))) { + continue; + } + + foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) { + if ($joinColumn['name'] == $columnName) { + $targetColumnName = $joinColumn['referencedColumnName']; + $targetClass = $em->getClassMetadata($assoc['targetEntity']); + + return self::getTypeOfColumn($targetColumnName, $targetClass, $em); + } + } + } + + throw new \RuntimeException(sprintf( + 'Could not resolve type of column "%s" of class "%s"', + $columnName, + $class->getName() + )); + } +} 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 00000000000..284f39bb845 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Class to store and retrieve the version of Doctrine + * + * + * @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.5.4'; + + /** + * 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/kenjis/ci-phpunit-test/.gitignore b/vendor/kenjis/ci-phpunit-test/.gitignore new file mode 100644 index 00000000000..4973c5f2297 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/.gitignore @@ -0,0 +1,4 @@ +/nbproject/ +/application/tests/build/ +/application/tests/_ci_phpunit_test/tmp/ +/application/tests/_ci_phpunit_test/patcher/debug.log diff --git a/vendor/kenjis/ci-phpunit-test/.scrutinizer.yml b/vendor/kenjis/ci-phpunit-test/.scrutinizer.yml new file mode 100644 index 00000000000..660cda10f34 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/.scrutinizer.yml @@ -0,0 +1,5 @@ +filter: + excluded_paths: + - 'bin/*' + - 'docs/*' + - 'application/tests/_ci_phpunit_test/patcher/third_party/*' diff --git a/vendor/kenjis/ci-phpunit-test/.travis.yml b/vendor/kenjis/ci-phpunit-test/.travis.yml new file mode 100644 index 00000000000..3cdb3b60c2c --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/.travis.yml @@ -0,0 +1,31 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7 + +before_script: + - pwd + - export cwd=`pwd` + - composer self-update + - cd .. + - pwd + - git clone https://github.com/kenjis/ci-app-for-ci-phpunit-test.git + - cd ci-app-for-ci-phpunit-test + - rm application/tests/_ci_phpunit_test + - composer update + - mv vendor/kenjis/ci-phpunit-test vendor/kenjis/ci-phpunit-test.tmp + - mv "$cwd" vendor/kenjis/ci-phpunit-test + - php install.php + +script: + - cd application/tests + - ../../vendor/bin/phpunit --coverage-text + +after_script: + - cd ../.. + - if [[ "$TRAVIS_PHP_VERSION" == "5.4" ]]; then php vendor/bin/coveralls -v; fi + - if [[ "$TRAVIS_PHP_VERSION" == "5.5" ]]; then php vendor/bin/coveralls -v; fi + - if [[ "$TRAVIS_PHP_VERSION" == "5.6" ]]; then php vendor/bin/coveralls -v; fi diff --git a/vendor/kenjis/ci-phpunit-test/CONTRIBUTING.md b/vendor/kenjis/ci-phpunit-test/CONTRIBUTING.md new file mode 100644 index 00000000000..0bd9c69daf4 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing to ci-phpunit-test + +You must write commit logs and pull requests in English. +Because most users could read English, but can't read most of other languages at all. + +## Testing + +There are tests for ci-phpunit-test: https://github.com/kenjis/ci-app-for-ci-phpunit-test/tree/master/application/tests + +Please write tests for your code and send Pull Request to it, if you could. + +## Documentation + +Update documentation if it is needed. + +* [README.md](README.md) +* [docs](docs) diff --git a/vendor/kenjis/ci-phpunit-test/Installer.php b/vendor/kenjis/ci-phpunit-test/Installer.php new file mode 100644 index 00000000000..9edbc27eefd --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/Installer.php @@ -0,0 +1,145 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class Installer +{ + const TEST_FOLDER = 'tests'; + + public static function install($app = 'application') + { + self::recursiveCopy( + dirname(__FILE__) . '/application/tests', + $app . '/' . static::TEST_FOLDER + ); + self::fixPath($app); + } + + /** + * Fix paths in Bootstrap.php + */ + private static function fixPath($app = 'application') + { + $file = $app . '/' . static::TEST_FOLDER . '/Bootstrap.php'; + $contents = file_get_contents($file); + + if (! file_exists('system')) { + if (file_exists('vendor/codeigniter/framework/system')) { + $contents = str_replace( + '$system_path = \'../../system\';', + '$system_path = \'../../vendor/codeigniter/framework/system\';', + $contents + ); + } else { + throw new Exception('Can\'t find "system" folder.'); + } + } + + if (! file_exists('index.php')) { + if (file_exists('public/index.php')) { + // CodeIgniter 3.0.6 and after + $contents = str_replace( + "define('FCPATH', realpath(dirname(__FILE__).'/../..').DIRECTORY_SEPARATOR);", + "define('FCPATH', realpath(dirname(__FILE__).'/../../public').DIRECTORY_SEPARATOR);", + $contents + ); + // CodeIgniter 3.0.5 and before + $contents = str_replace( + "define('FCPATH', realpath(dirname(__FILE__).'/../..').'/');", + "define('FCPATH', realpath(dirname(__FILE__).'/../../public').'/');", + $contents + ); + } elseif (file_exists($app . '/public/index.php')) { + // CodeIgniter 3.0.6 and after + $contents = str_replace( + "define('FCPATH', realpath(dirname(__FILE__).'/../..').DIRECTORY_SEPARATOR);", + "define('FCPATH', realpath(dirname(__FILE__).'/../public').DIRECTORY_SEPARATOR);", + $contents + ); + // CodeIgniter 3.0.5 and before + $contents = str_replace( + "define('FCPATH', realpath(dirname(__FILE__).'/../..').'/');", + "define('FCPATH', realpath(dirname(__FILE__).'/../public').'/');", + $contents + ); + if ($app != 'application') { + $contents = str_replace( + "\$application_folder = '../../application';", + "\$application_folder = '../../{$app}';", + $contents + ); + } + } else { + throw new Exception('Can\'t find "index.php".'); + } + } + + file_put_contents($file, $contents); + } + + public static function update($app = 'application') + { + $target_dir = $app . '/' . static::TEST_FOLDER . '/_ci_phpunit_test'; + self::recursiveUnlink($target_dir); + self::recursiveCopy( + dirname(__FILE__) . '/application/tests/_ci_phpunit_test', + $target_dir + ); + } + + /** + * Recursive Copy + * + * @param string $src + * @param string $dst + */ + private static function recursiveCopy($src, $dst) + { + @mkdir($dst, 0755); + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($src, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST + ); + + foreach ($iterator as $file) { + if ($file->isDir()) { + @mkdir($dst . '/' . $iterator->getSubPathName()); + } else { + $success = copy($file, $dst . '/' . $iterator->getSubPathName()); + if ($success) { + echo 'copied: ' . $dst . '/' . $iterator->getSubPathName() . PHP_EOL; + } + } + } + } + + /** + * Recursive Unlink + * + * @param string $dir + */ + private static function recursiveUnlink($dir) + { + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($iterator as $file) { + if ($file->isDir()) { + rmdir($file); + } else { + unlink($file); + } + } + + rmdir($dir); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/LICENSE.md b/vendor/kenjis/ci-phpunit-test/LICENSE.md new file mode 100644 index 00000000000..f5717c21f0e --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2015 Kenji Suzuki + +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. \ No newline at end of file diff --git a/vendor/kenjis/ci-phpunit-test/README.md b/vendor/kenjis/ci-phpunit-test/README.md new file mode 100644 index 00000000000..cf1df8f8d43 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/README.md @@ -0,0 +1,188 @@ +# ci-phpunit-test for CodeIgniter 3.0 + +[![Latest Stable Version](https://poser.pugx.org/kenjis/ci-phpunit-test/v/stable)](https://packagist.org/packages/kenjis/ci-phpunit-test) [![Total Downloads](https://poser.pugx.org/kenjis/ci-phpunit-test/downloads)](https://packagist.org/packages/kenjis/ci-phpunit-test) [![Latest Unstable Version](https://poser.pugx.org/kenjis/ci-phpunit-test/v/unstable)](https://packagist.org/packages/kenjis/ci-phpunit-test) [![License](https://poser.pugx.org/kenjis/ci-phpunit-test/license)](https://packagist.org/packages/kenjis/ci-phpunit-test) + +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/kenjis/ci-phpunit-test/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/kenjis/ci-phpunit-test/?branch=master) +[![Coverage Status](https://coveralls.io/repos/kenjis/ci-phpunit-test/badge.svg?branch=master)](https://coveralls.io/r/kenjis/ci-phpunit-test?branch=master) +[![Build Status](https://travis-ci.org/kenjis/ci-phpunit-test.svg?branch=master)](https://travis-ci.org/kenjis/ci-phpunit-test) + +An easier way to use PHPUnit with [CodeIgniter](https://github.com/bcit-ci/CodeIgniter) 3.0. + +* You don't have to modify CodeIgniter core files at all. +* You can write controller tests easily. +* Nothing is untestable, maybe. +* Well documented. + +![Screenshot: Running tests on NetBeans 8.1](https://pbs.twimg.com/media/CUUmhxWVAAAwx3b.png) + +## Requirements + +* PHP 5.4.0 or later +* CodeIgniter 3.0.* +* PHPUnit 4.3 or later (4.7 or later is recommended) + * If you use NetBeans 8.0.2, please use 4.7. 4.8 is not compatible yet. You can download old version of `phpunit.phar` from . + +## Optional + +* NetBeans + * Go to *Project Properties > Testing > PHPUnit*, check *Use Custom Test Suite* checkbox, and select `application/tests/_ci_phpunit_test/TestSuiteProvider.php`. + +## Change Log + +See [Change Log](https://github.com/kenjis/ci-phpunit-test/blob/master/docs/ChangeLog.md). + +## Folder Structure + +~~~ +codeigniter/ +├── application/ +│   └── tests/ +│      ├── _ci_phpunit_test/ ... don't touch! files ci-phpunit-test uses +│      ├── Bootstrap.php ... bootstrap file for PHPUnit +│      ├── TestCase.php ... TestCase class +│      ├── controllers/ ... put your controller tests +│      ├── libraries/ ... put your library tests +│      ├── mocks/ +│      │   └── libraries/ ... mock libraries +│      ├── models/ ... put your model tests +│     └── phpunit.xml ... config file for PHPUnit +└── vendor/ +~~~ + +## Installation + +Download latest `ci-phpunit-test`: https://github.com/kenjis/ci-phpunit-test/releases + +Unzip and copy `application/tests` folder into your `application` folder in CodeIgniter project. That's it. + +If you like Composer: + +~~~ +$ cd /path/to/codeigniter/ +$ composer require kenjis/ci-phpunit-test --dev +~~~ + +And run `install.php`: + +~~~ +$ php vendor/kenjis/ci-phpunit-test/install.php +~~~ + +* Above command always overwrites exisiting files. +* You must run it at CodeIgniter project root folder. + +## Upgrading + +Download latest `ci-phpunit-test`: https://github.com/kenjis/ci-phpunit-test/releases + +Unzip and replace `application/tests/_ci_phpunit_test` folder. + +If you like Composer: + +~~~ +$ cd /path/to/codeigniter/ +$ composer update kenjis/ci-phpunit-test +$ php vendor/kenjis/ci-phpunit-test/update.php +~~~ + +## How to Run Tests + +You have to install PHPUnit before running tests. + +**Note:** You must run `phpunit` command in `application/tests` folder. + +~~~ +$ cd /path/to/codeigniter/ +$ cd application/tests/ +$ phpunit +PHPUnit 4.7.7 by Sebastian Bergmann and contributors. + +... + +Time: 341 ms, Memory: 5.50Mb + +OK (3 tests, 3 assertions) + +Generating code coverage report in Clover XML format ... done + +Generating code coverage report in HTML format ... done +~~~ + +To generate coverage report, Xdebug is needed. + +If you want to run a single test case file: + +~~~ +$ phpunit models/Category_model_test.php +~~~ + +## How to Write Tests + +As an example, a test case class for `Inventory_model` would be as follows: + +~~~php +resetInstance(); + $this->CI->load->model('Inventory_model'); + $this->obj = $this->CI->Inventory_model; + } + + public function test_get_category_list() + { + $expected = [ + 1 => 'Book', + 2 => 'CD', + 3 => 'DVD', + ]; + $list = $this->obj->get_category_list(); + foreach ($list as $category) { + $this->assertEquals($expected[$category->id], $category->name); + } + } + + public function test_get_category_name() + { + $actual = $this->obj->get_category_name(1); + $expected = 'Book'; + $this->assertEquals($expected, $actual); + } +} +~~~ + +As an example, a test case class for Welcome controller would be as follows: + +~~~php +request('GET', 'welcome/index'); + $this->assertContains( + 'Welcome to CodeIgniter', $output + ); + } +} +~~~ + +See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/master/docs/HowToWriteTests.md) for details. + +## Function/Class Reference + +See [Function and Class Reference](https://github.com/kenjis/ci-phpunit-test/blob/master/docs/FunctionAndClassReference.md). + +## Related Projects for CodeIgniter 3.0 + +* [CodeIgniter Composer Installer](https://github.com/kenjis/codeigniter-composer-installer) +* [Cli for CodeIgniter 3.0](https://github.com/kenjis/codeigniter-cli) +* [CodeIgniter Simple and Secure Twig](https://github.com/kenjis/codeigniter-ss-twig) +* [CodeIgniter Doctrine](https://github.com/kenjis/codeigniter-doctrine) +* [CodeIgniter Deployer](https://github.com/kenjis/codeigniter-deployer) +* [CodeIgniter3 Filename Checker](https://github.com/kenjis/codeigniter3-filename-checker) +* [CodeIgniter Widget (View Partial) Sample](https://github.com/kenjis/codeigniter-widgets) diff --git a/vendor/kenjis/ci-phpunit-test/application/database/seeds/CategorySeeder.php b/vendor/kenjis/ci-phpunit-test/application/database/seeds/CategorySeeder.php new file mode 100644 index 00000000000..410f7c14fa6 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/database/seeds/CategorySeeder.php @@ -0,0 +1,38 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CategorySeeder extends Seeder { + + private $table = 'category'; + + public function run() + { + $this->db->truncate($this->table); + + $data = [ + 'id' => 1, + 'name' => 'Book', + ]; + $this->db->insert($this->table, $data); + + $data = [ + 'id' => 2, + 'name' => 'CD', + ]; + $this->db->insert($this->table, $data); + + $data = [ + 'id' => 3, + 'name' => 'DVD', + ]; + $this->db->insert($this->table, $data); + } + +} diff --git a/vendor/kenjis/ci-phpunit-test/application/libraries/Seeder.php b/vendor/kenjis/ci-phpunit-test/application/libraries/Seeder.php new file mode 100644 index 00000000000..48bbd4a818b --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/libraries/Seeder.php @@ -0,0 +1,43 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class Seeder +{ + private $CI; + protected $db; + protected $dbforge; + + public function __construct() + { + $this->CI =& get_instance(); + $this->CI->load->database(); + $this->CI->load->dbforge(); + $this->db = $this->CI->db; + $this->dbforge = $this->CI->dbforge; + } + + /** + * Run another seeder + * + * @param string $seeder Seeder classname + */ + public function call($seeder) + { + $file = APPPATH . 'database/seeds/' . $seeder . '.php'; + require_once $file; + $obj = new $seeder; + $obj->run(); + } + + public function __get($property) + { + return $this->CI->$property; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/libraries/Session/MY_Session.php b/vendor/kenjis/ci-phpunit-test/application/libraries/Session/MY_Session.php new file mode 100644 index 00000000000..b1bcb94eeba --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/libraries/Session/MY_Session.php @@ -0,0 +1,26 @@ +=')) + { + error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED); + } + else + { + error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE); + } + break; + + default: + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'The application environment is not set correctly.'; + exit(1); // EXIT_ERROR +} + +/* + *--------------------------------------------------------------- + * SYSTEM DIRECTORY NAME + *--------------------------------------------------------------- + * + * This variable must contain the name of your "system" directory. + * Set the path if it is not in the same directory as this file. + */ + $system_path = '../../system'; + +/* + *--------------------------------------------------------------- + * APPLICATION DIRECTORY NAME + *--------------------------------------------------------------- + * + * If you want this front controller to use a different "application" + * directory than the default one you can set its name here. The directory + * can also be renamed or relocated anywhere on your server. If you do, + * use an absolute (full) server path. + * For more info please see the user guide: + * + * https://codeigniter.com/user_guide/general/managing_apps.html + * + * NO TRAILING SLASH! + */ + $application_folder = '../../application'; + +/* + *--------------------------------------------------------------- + * VIEW DIRECTORY NAME + *--------------------------------------------------------------- + * + * If you want to move the view directory out of the application + * directory, set the path to it here. The directory can be renamed + * and relocated anywhere on your server. If blank, it will default + * to the standard location inside your application directory. + * If you do move this, use an absolute (full) server path. + * + * NO TRAILING SLASH! + */ + $view_folder = ''; + + +/* + * -------------------------------------------------------------------- + * DEFAULT CONTROLLER + * -------------------------------------------------------------------- + * + * Normally you will set your default controller in the routes.php file. + * You can, however, force a custom routing by hard-coding a + * specific controller class/function here. For most applications, you + * WILL NOT set your routing here, but it's an option for those + * special instances where you might want to override the standard + * routing in a specific front controller that shares a common CI installation. + * + * IMPORTANT: If you set the routing here, NO OTHER controller will be + * callable. In essence, this preference limits your application to ONE + * specific controller. Leave the function name blank if you need + * to call functions dynamically via the URI. + * + * Un-comment the $routing array below to use this feature + */ + // The directory name, relative to the "controllers" directory. Leave blank + // if your controller is not in a sub-directory within the "controllers" one + // $routing['directory'] = ''; + + // The controller class file name. Example: mycontroller + // $routing['controller'] = ''; + + // The controller function you wish to be called. + // $routing['function'] = ''; + + +/* + * ------------------------------------------------------------------- + * CUSTOM CONFIG VALUES + * ------------------------------------------------------------------- + * + * The $assign_to_config array below will be passed dynamically to the + * config class when initialized. This allows you to set custom config + * items or override any default config values found in the config.php file. + * This can be handy as it permits you to share one application between + * multiple front controller files, with each file containing different + * config values. + * + * Un-comment the $assign_to_config array below to use this feature + */ + // $assign_to_config['name_of_config_item'] = 'value of config item'; + + + +// -------------------------------------------------------------------- +// END OF USER CONFIGURABLE SETTINGS. DO NOT EDIT BELOW THIS LINE +// -------------------------------------------------------------------- + +/* + * --------------------------------------------------------------- + * Resolve the system path for increased reliability + * --------------------------------------------------------------- + */ + + // Set the current directory correctly for CLI requests +// if (defined('STDIN')) +// { + // This is needed for @runInSeparateProcess + chdir(dirname(__FILE__)); +// } + + if (($_temp = realpath($system_path)) !== FALSE) + { + $system_path = $_temp.DIRECTORY_SEPARATOR; + } + else + { + // Ensure there's a trailing slash + $system_path = strtr( + rtrim($system_path, '/\\'), + '/\\', + DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR + ).DIRECTORY_SEPARATOR; + + } + + // Is the system path correct? + if ( ! is_dir($system_path)) + { + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'Your system folder path does not appear to be set correctly. Please open the following file and correct this: '.pathinfo(__FILE__, PATHINFO_BASENAME); + exit(3); // EXIT_CONFIG + } + +/* + * ------------------------------------------------------------------- + * Now that we know the path, set the main path constants + * ------------------------------------------------------------------- + */ + // The name of THIS file + define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME)); + + // Path to the system directory + define('BASEPATH', $system_path); + + // Path to the front controller (this file) directory + define('FCPATH', realpath(dirname(__FILE__).'/../..').DIRECTORY_SEPARATOR); + + // Name of the "system" directory + define('SYSDIR', basename(BASEPATH)); + + // The path to the "application" directory + if (is_dir($application_folder)) + { + if (($_temp = realpath($application_folder)) !== FALSE) + { + $application_folder = $_temp; + } + else + { + $application_folder = strtr( + rtrim($application_folder, '/\\'), + '/\\', + DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR + ); + } + } + elseif (is_dir(BASEPATH.$application_folder.DIRECTORY_SEPARATOR)) + { + $application_folder = BASEPATH.strtr( + trim($application_folder, '/\\'), + '/\\', + DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR + ); + } + else + { + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'Your application folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF; + exit(3); // EXIT_CONFIG + } + + define('APPPATH', $application_folder.DIRECTORY_SEPARATOR); + + // The path to the "views" directory + if ( ! isset($view_folder[0]) && is_dir(APPPATH.'views'.DIRECTORY_SEPARATOR)) + { + $view_folder = APPPATH.'views'; + } + elseif (is_dir($view_folder)) + { + if (($_temp = realpath($view_folder)) !== FALSE) + { + $view_folder = $_temp; + } + else + { + $view_folder = strtr( + rtrim($view_folder, '/\\'), + '/\\', + DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR + ); + } + } + elseif (is_dir(APPPATH.$view_folder.DIRECTORY_SEPARATOR)) + { + $view_folder = APPPATH.strtr( + trim($view_folder, '/\\'), + '/\\', + DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR + ); + } + else + { + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'Your view folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF; + exit(3); // EXIT_CONFIG + } + + define('VIEWPATH', $view_folder.DIRECTORY_SEPARATOR); + +/* + * ------------------------------------------------------------------- + * Enabling Monkey Patching + * ------------------------------------------------------------------- + * + * If you want to use monkey patching, uncomment below code and configure + * for your application. + */ +/* +require __DIR__ . '/_ci_phpunit_test/patcher/bootstrap.php'; +MonkeyPatchManager::init([ + // PHP Parser: PREFER_PHP7, PREFER_PHP5, ONLY_PHP7, ONLY_PHP5 + 'php_parser' => 'PREFER_PHP5', + 'cache_dir' => APPPATH . 'tests/_ci_phpunit_test/tmp/cache', + // Directories to patch source files + 'include_paths' => [ + APPPATH, + BASEPATH, + APPPATH . 'tests/_ci_phpunit_test/replacing/', + ], + // Excluding directories to patch + // If you want to patch files inside paths below, you must add the directory starting with '-' + 'exclude_paths' => [ + APPPATH . 'tests/', + '-' . APPPATH . 'tests/_ci_phpunit_test/replacing/', + ], + // All patchers you use. + 'patcher_list' => [ + 'ExitPatcher', + 'FunctionPatcher', + 'MethodPatcher', + 'ConstantPatcher', + ], + // Additional functions to patch + 'functions_to_patch' => [ + //'random_string', + ], + 'exit_exception_classname' => 'CIPHPUnitTestExitException', +]); +*/ + +/* + * ------------------------------------------------------------------- + * Added for ci-phpunit-test + * ------------------------------------------------------------------- + */ +require __DIR__ . '/_ci_phpunit_test/CIPHPUnitTest.php'; +CIPHPUnitTest::init(); +/* + * Or you can set directories for autoloading + */ +/* +CIPHPUnitTest::init([ + // Directories for autoloading + APPPATH.'models', + APPPATH.'libraries', + APPPATH.'controllers', + APPPATH.'modules', +]); +*/ diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/TestCase.php b/vendor/kenjis/ci-phpunit-test/application/tests/TestCase.php new file mode 100644 index 00000000000..56783d47eac --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/TestCase.php @@ -0,0 +1,5 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTest +{ + private static $loader_class = 'CI_Loader'; + private static $autoload_dirs; + + /** + * Initialize CIPHPUnitTest + * + * @param array $autoload_dirs directories to search class file for autoloader + */ + public static function init(array $autoload_dirs = null) + { + // Fix CLI args + $_server_backup = $_SERVER; + $_SERVER['argv'] = [ + 'index.php', + 'welcome' // Dummy + ]; + $_SERVER['argc'] = 2; + + self::$autoload_dirs = $autoload_dirs; + + $cwd_backup = getcwd(); + + // Load autoloader for ci-phpunit-test + require __DIR__ . '/autoloader.php'; + + // Autoloader for PHP-Parser + // Don't use `require`, because we may have required already + // in `patcher/bootstrap.php` + require_once __DIR__ . '/patcher/third_party/PHP-Parser/lib/bootstrap.php'; + + require APPPATH . '/tests/TestCase.php'; + + // Replace a few Common functions + require __DIR__ . '/replacing/core/Common.php'; + require BASEPATH . 'core/Common.php'; + + // Workaround for missing CodeIgniter's error handler + // See https://github.com/kenjis/ci-phpunit-test/issues/37 + set_error_handler('_error_handler'); + + // Load new functions of CIPHPUnitTest + require __DIR__ . '/functions.php'; + // Load ci-phpunit-test CI_Loader + require __DIR__ . '/replacing/core/Loader.php'; + // Load ci-phpunit-test CI_Input + require __DIR__ . '/replacing/core/Input.php'; + + // Change current directroy + chdir(FCPATH); + + /* + * -------------------------------------------------------------------- + * LOAD THE BOOTSTRAP FILE + * -------------------------------------------------------------------- + * + * And away we go... + */ + require __DIR__ . '/replacing/core/CodeIgniter.php'; + + self::replaceHelpers(); + + // Create CodeIgniter instance + if (! self::wiredesignzHmvcInstalled()) + { + new CI_Controller(); + } + else + { + new MX_Controller(); + } + + // This code is here, not to cause errors with HMVC + self::replaceLoader(); + + // Restore $_SERVER. We need this for NetBeans + $_SERVER = $_server_backup; + + // Restore cwd to use `Usage: phpunit [options] ` + chdir($cwd_backup); + } + + public static function createCodeIgniterInstance() + { + if (! self::wiredesignzHmvcInstalled()) + { + new CI_Controller(); + } + else + { + new CI(); + new MX_Controller(); + } + } + + public static function wiredesignzHmvcInstalled() + { + if (file_exists(APPPATH.'third_party/MX')) + { + return true; + } + + return false; + } + + public static function getAutoloadDirs() + { + return self::$autoload_dirs; + } + + protected static function replaceLoader() + { + $my_loader_file = + APPPATH . 'core/' . config_item('subclass_prefix') . 'Loader.php'; + + if (file_exists($my_loader_file)) + { + self::$loader_class = config_item('subclass_prefix') . 'Loader'; + if ( ! class_exists(self::$loader_class)) + { + require $my_loader_file; + } + } + self::loadLoader(); + } + + protected static function replaceHelpers() + { + $my_helper_file = APPPATH . 'helpers/' . config_item('subclass_prefix') . 'url_helper.php'; + if (file_exists($my_helper_file)) + { + require $my_helper_file; + } + require __DIR__ . '/replacing/helpers/url_helper.php'; + } + + public static function setPatcherCacheDir($dir = null) + { + if ($dir === null) + { + $dir = APPPATH . 'tests/_ci_phpunit_test/tmp/cache'; + } + + MonkeyPatchManager::setCacheDir( + $dir + ); + } + + public static function loadLoader() + { + $loader = new self::$loader_class; + load_class_instance('Loader', $loader); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestAutoloader.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestAutoloader.php new file mode 100644 index 00000000000..b1bd71a97fb --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestAutoloader.php @@ -0,0 +1,163 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestAutoloader +{ + private $alias = [ + 'MonkeyPatch', + 'ReflectionHelper', + ]; + + /** + * @var directories to search file + */ + private $dirs = []; + + /** + * @var CIPHPUnitTestFileCache + */ + private $cache; + + /** + * @param CIPHPUnitTestFileCache $cache + * @param array $dirs directories to search file + */ + public function __construct( + CIPHPUnitTestFileCache $cache = null, + array $dirs = null + ) + { + $this->cache = $cache; + if ($dirs === null) + { + $this->dirs = [ + APPPATH.'models', + APPPATH.'libraries', + APPPATH.'controllers', + APPPATH.'modules', + ]; + } + else + { + $this->dirs = $dirs; + } + } + + public function load($class) + { + if ($this->cache) + { + if ($this->loadFromCache($class)) + { + return; + } + } + + $this->loadCIPHPUnitTestAliasClass($class); + $this->loadCIPHPUnitTestClass($class); + $this->loadApplicationClass($class); + } + + protected function loadCIPHPUnitTestAliasClass($class) + { + if (in_array($class, $this->alias)) + { + $dir = __DIR__ . '/alias'; + $this->loadClassFile($dir, $class); + } + } + + protected function loadCIPHPUnitTestClass($class) + { + if (substr($class, 0, 13) !== 'CIPHPUnitTest') + { + return; + } + + if (substr($class, -9) !== 'Exception') + { + $dir = __DIR__; + $this->loadClassFile($dir, $class); + } + else + { + $dir = __DIR__ . '/exceptions'; + $this->loadClassFile($dir, $class); + } + } + + protected function loadApplicationClass($class) + { + foreach ($this->dirs as $dir) + { + if ( ! is_dir($dir)) + { + continue; + } + + if ($this->loadClassFile($dir, $class)) + { + return true; + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator( + $dir, \RecursiveDirectoryIterator::SKIP_DOTS + ), + \RecursiveIteratorIterator::SELF_FIRST + ); + + foreach ($iterator as $file) + { + if ($file->isDir()) + { + if ($this->loadClassFile($file, $class)) + { + return true; + } + } + } + } + } + + protected function loadClassFile($dir, $class) + { + $class_file = $dir . '/' . $class . '.php'; + if (file_exists($class_file)) + { + require $class_file; + if ($this->cache) + { + $this->cache[$class] = $class_file; + } + return true; + } + + return false; + } + + protected function loadFromCache($class) + { + if ($filename = $this->cache[$class]) + { + if (is_readable($filename)) + { + require $filename; + return true; + } + else + { + unset($this->cache[$class]); + } + } + + return false; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php new file mode 100644 index 00000000000..b34a1b9e0a9 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestCase.php @@ -0,0 +1,369 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +/** + * @property CIPHPUnitTestRequest $request + * @property CIPHPUnitTestDouble $double + * @property CIPHPUnitTestReflection $reflection + */ +class CIPHPUnitTestCase extends PHPUnit_Framework_TestCase +{ + protected $_error_reporting = -1; + + /** + * @var CI_Controller CodeIgniter instance + */ + protected $CI; + + protected $class_map = [ + 'request' => 'CIPHPUnitTestRequest', + 'double' => 'CIPHPUnitTestDouble', + 'reflection' => 'CIPHPUnitTestReflection', + ]; + + public function setCI(CI_Controller $CI) + { + $this->CI = $CI; + } + + public function __get($name) + { + if (isset($this->class_map[$name])) + { + $this->$name = new $this->class_map[$name]($this); + return $this->$name; + } + + throw new LogicException('No such property: ' . $name); + } + + public static function setUpBeforeClass() + { + // Fix CLI args, because you may set invalid URI characters + // For example, when you run tests on NetBeans + $_SERVER['argv'] = [ + 'index.php', + ]; + $_SERVER['argc'] = 1; + + // Reset current directroy + chdir(FCPATH); + } + + /** + * Reset CodeIgniter instance and assign new CodeIgniter instance as $this->CI + */ + public function resetInstance() + { + reset_instance(); + CIPHPUnitTest::createCodeIgniterInstance(); + $this->CI =& get_instance(); + } + + protected function tearDown() + { + if (class_exists('MonkeyPatch', false)) + { + if (MonkeyPatchManager::isEnabled('FunctionPatcher')) + { + try { + MonkeyPatch::verifyFunctionInvocations(); + } catch (Exception $e) { + MonkeyPatch::resetFunctions(); + throw $e; + } + + MonkeyPatch::resetFunctions(); + } + + if (MonkeyPatchManager::isEnabled('ConstantPatcher')) + { + MonkeyPatch::resetConstants(); + } + + if (MonkeyPatchManager::isEnabled('MethodPatcher')) + { + try { + MonkeyPatch::verifyMethodInvocations(); + } catch (Exception $e) { + MonkeyPatch::resetMethods(); + throw $e; + } + + MonkeyPatch::resetMethods(); + } + } + } + + /** + * Request to Controller + * + * @param string $http_method HTTP method + * @param array|string $argv array of controller,method,arg|uri + * @param array $params POST parameters/Query string + */ + public function request($http_method, $argv, $params = []) + { + return $this->request->request($http_method, $argv, $params); + } + + /** + * Request to Controller using ajax request + * + * @param string $http_method HTTP method + * @param array|string $argv array of controller,method,arg|uri + * @param array $params POST parameters/Query string + */ + public function ajaxRequest($http_method, $argv, $params = []) + { + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest'; + return $this->request($http_method, $argv, $params); + } + + /** + * Get Mock Object + * + * $email = $this->getMockBuilder('CI_Email') + * ->setMethods(['send']) + * ->getMock(); + * $email->method('send')->willReturn(TRUE); + * + * will be + * + * $email = $this->getDouble('CI_Email', ['send' => TRUE]); + * + * @param string $classname + * @param array $params [method_name => return_value] + * @param bool $enable_constructor enable constructor or not + * @return object PHPUnit mock object + */ + public function getDouble($classname, $params, $enable_constructor = false) + { + return $this->double->getDouble($classname, $params, $enable_constructor); + } + + /** + * Verifies that method was called exactly $times times + * + * $loader->expects($this->exactly(2)) + * ->method('view') + * ->withConsecutive( + * ['shop_confirm', $this->anything(), TRUE], + * ['shop_tmpl_checkout', $this->anything()] + * ); + * + * will be + * + * $this->verifyInvokedMultipleTimes( + * $loader, + * 'view', + * 2, + * [ + * ['shop_confirm', $this->anything(), TRUE], + * ['shop_tmpl_checkout', $this->anything()] + * ] + * ); + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param int $times + * @param array $params arguments + */ + public function verifyInvokedMultipleTimes($mock, $method, $times, $params = null) + { + $this->double->verifyInvokedMultipleTimes( + $mock, $method, $times, $params + ); + } + + /** + * Verifies a method was invoked at least once + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param array $params arguments + */ + public function verifyInvoked($mock, $method, $params = null) + { + $this->double->verifyInvoked($mock, $method, $params); + } + + /** + * Verifies that method was invoked only once + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param array $params arguments + */ + public function verifyInvokedOnce($mock, $method, $params = null) + { + $this->double->verifyInvokedOnce($mock, $method, $params); + } + + /** + * Verifies that method was not called + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param array $params arguments + */ + public function verifyNeverInvoked($mock, $method, $params = null) + { + $this->double->verifyNeverInvoked($mock, $method, $params); + } + + public function warningOff() + { + $this->_error_reporting = error_reporting( + E_ALL & ~E_WARNING & ~E_NOTICE + ); + } + + public function warningOn() + { + error_reporting($this->_error_reporting); + } + + /** + * Asserts HTTP response code + * + * @param int $code + */ + public function assertResponseCode($code) + { + $status = $this->request->getStatus(); + $actual = $status['code']; + + $this->assertSame( + $code, + $actual, + 'Status code is not ' . $code . ' but ' . $actual . '.' + ); + } + + /** + * Asserts HTTP response header + * + * @param string $name header name + * @param string $value header value + */ + public function assertResponseHeader($name, $value) + { + $CI =& get_instance(); + $actual = $CI->output->get_header($name); + + if ($actual === null) + { + $this->fail("The '$name' header is not set.\nNote that `assertResponseHeader()` can only assert headers set by `\$this->output->set_header()`"); + } + + $this->assertEquals( + $value, + $actual, + "The '$name' header is not '$value' but '$actual'." + ); + } + + /** + * Asserts HTTP response cookie + * + * @param string $name cookie name + * @param string|array $value cookie value|array of cookie params + * @param bool $allow_duplicate whether to allow duplicated cookies + */ + public function assertResponseCookie($name, $value, $allow_duplicate = false) + { + $CI =& get_instance(); + $cookies = isset($CI->output->_cookies[$name]) + ? $CI->output->_cookies[$name] : null; + + if ($cookies === null) + { + $this->fail("The cookie '$name' is not set.\nNote that `assertResponseCookie()` can only assert cookies set by `\$this->input->set_cookie()`"); + } + + $count = count($cookies); + if ($count > 1 && ! $allow_duplicate) + { + $values = []; + foreach ($cookies as $key => $val) + { + $values[] = "'{$val['value']}'"; + } + $values = implode(' and ', $values); + $this->fail("You have more than one cookie '$name'. The values are $values.\nIf it is okay, please set `true` as the 3rd argument of `assertResponseCookie()`"); + } + + // Get the last cookie + $cookie = $cookies[$count - 1]; + if (is_string($value)) + { + $this->assertEquals( + $value, + $cookie['value'], + "The cookie '$name' value is not '$value' but '{$cookie['value']}'." + ); + return; + } + + foreach ($value as $key => $val) + { + $this->assertEquals( + $value[$key], + $cookie[$key], + "The cookie '$name' $key is not '{$value[$key]}' but '{$cookie[$key]}'." + ); + } + } + + /** + * Asserts Redirect + * + * @param string $uri URI to redirect + * @param int $code response code + */ + public function assertRedirect($uri, $code = null) + { + $status = $this->request->getStatus(); + + if ($status['redirect'] === null) + { + $this->fail('redirect() is not called.'); + } + + if (! function_exists('site_url')) + { + $CI =& get_instance(); + $CI->load->helper('url'); + } + + if (! preg_match('#^(\w+:)?//#i', $uri)) + { + $uri = site_url($uri); + } + $absolute_url = $uri; + $expected = 'Redirect to ' . $absolute_url; + + $this->assertSame( + $expected, + $status['redirect'], + 'URL to redirect is not ' . $expected . ' but ' . $status['redirect'] . '.' + ); + + if ($code !== null) + { + $this->assertSame( + $code, + $status['code'], + 'Status code is not ' . $code . ' but ' . $status['code'] . '.' + ); + } + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestDouble.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestDouble.php new file mode 100644 index 00000000000..2cc099db0da --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestDouble.php @@ -0,0 +1,179 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestDouble +{ + protected $testCase; + + public function __construct(PHPUnit_Framework_TestCase $testCase) + { + $this->testCase = $testCase; + } + + /** + * Get Mock Object + * + * $email = $this->getMockBuilder('CI_Email') + * ->disableOriginalConstructor() + * ->setMethods(['send']) + * ->getMock(); + * $email->method('send')->willReturn(TRUE); + * + * will be + * + * $email = $this->getDouble('CI_Email', ['send' => TRUE]); + * + * @param string $classname + * @param array $params [method_name => return_value] + * @param bool $enable_constructor enable constructor or not + * @return object PHPUnit mock object + */ + public function getDouble($classname, $params, $enable_constructor = false) + { + $methods = array_keys($params); + + // `disableOriginalConstructor()` is the default, because if we call + // construnctor, it may call `$this->load->...` or other CodeIgniter + // methods in it. But we can't use them in + // `$this->request->setCallablePreConstructor()` + $mock = $this->testCase->getMockBuilder($classname); + if (! $enable_constructor) + { + $mock->disableOriginalConstructor(); + } + $mock = $mock->setMethods($methods)->getMock(); + + foreach ($params as $method => $return) + { + $mock->expects($this->testCase->any())->method($method) + ->willReturn($return); + } + + return $mock; + } + + protected function _verify($mock, $method, $params = null, $expects, $with) + { + $invocation = $mock->expects($expects) + ->method($method); + + $count = count($params); + + switch ($count) { + case 0: + break; + case 1: + $invocation->$with( + $params[0] + ); + break; + case 2: + $invocation->$with( + $params[0], $params[1] + ); + break; + case 3: + $invocation->$with( + $params[0], $params[1], $params[2] + ); + break; + case 4: + $invocation->$with( + $params[0], $params[1], $params[2], $params[3] + ); + break; + case 5: + $invocation->$with( + $params[0], $params[1], $params[2], $params[3], $params[4] + ); + break; + default: + throw new RuntimeException( + 'Sorry, ' . $count . ' params not implemented yet' + ); + } + } + + /** + * Verifies that method was called exactly $times times + * + * $loader->expects($this->exactly(2)) + * ->method('view') + * ->withConsecutive( + * ['shop_confirm', $this->anything(), TRUE], + * ['shop_tmpl_checkout', $this->anything()] + * ); + * + * will be + * + * $this->verifyInvokedMultipleTimes( + * $loader, + * 'view', + * 2, + * [ + * ['shop_confirm', $this->anything(), TRUE], + * ['shop_tmpl_checkout', $this->anything()] + * ] + * ); + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param int $times + * @param array $params arguments + */ + public function verifyInvokedMultipleTimes($mock, $method, $times, $params = null) + { + $this->_verify( + $mock, $method, $params, $this->testCase->exactly($times), 'withConsecutive' + ); + } + + /** + * Verifies a method was invoked at least once + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param array $params arguments + */ + public function verifyInvoked($mock, $method, $params = null) + { + $this->_verify( + $mock, $method, $params, $this->testCase->atLeastOnce(), 'with' + ); + } + + /** + * Verifies that method was invoked only once + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param array $params arguments + */ + public function verifyInvokedOnce($mock, $method, $params = null) + { + $this->_verify( + $mock, $method, $params, $this->testCase->once(), 'with' + ); + } + + /** + * Verifies that method was not called + * + * @param object $mock PHPUnit mock object + * @param string $method + * @param array $params arguments + */ + public function verifyNeverInvoked($mock, $method, $params = null) + { + $this->_verify( + $mock, $method, $params, $this->testCase->never(), 'with' + ); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestFileCache.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestFileCache.php new file mode 100644 index 00000000000..b695c2609bc --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestFileCache.php @@ -0,0 +1,86 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestFileCache implements ArrayAccess +{ + private $file; + private $map = []; + + public function __construct($file) + { + $this->file = $file; + + if (file_exists($this->file)) + { + $this->map = unserialize(file_get_contents($this->file)); + return; + } + + $dir = dirname($this->file); + if (! is_dir($dir)) + { + if (@mkdir($dir, 0777, true) === false) + { + throw new RuntimeException('Failed to create folder: ' . $dir); + } + } + + if (file_put_contents($this->file, '') === false) + { + throw new RuntimeException( + 'Failed to write to cache file: ' . $this->file + ); + } + } + + public function __destruct() + { + file_put_contents($this->file, serialize($this->map)); + } + + /** + * Dump cache data (sorted by key) + * + * @return array + */ + public function dump() + { + $map = $this->map; + ksort($map); + return $map; + } + + public function offsetSet($key, $value) + { + $this->map[$key] = $value; + } + + public function offsetGet($key) + { + if ($this->offsetExists($key)) + { + return $this->map[$key]; + } + else + { + return null; + } + } + + public function offsetExists($key) + { + return isset($this->map[$key]); + } + + public function offsetUnset($key) + { + unset($this->map[$key]); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestNullCodeIgniter.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestNullCodeIgniter.php new file mode 100644 index 00000000000..3597d47a124 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestNullCodeIgniter.php @@ -0,0 +1,20 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +/** + * Represents CodeIgniter instance is null + */ +class CIPHPUnitTestNullCodeIgniter +{ + public function __get($name) + { + throw new LogicException("CodeIgniter instance is not instantiated yet. You can't use `\$this->$name` at the moment. Please fix your test code."); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestReflection.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestReflection.php new file mode 100644 index 00000000000..dc9303c8b57 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestReflection.php @@ -0,0 +1,65 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestReflection +{ + /** + * @param object|string $obj object or class name + * @param string $method method name + * @return closure + */ + public static function getPrivateMethodInvoker($obj, $method) + { + $ref_method = new ReflectionMethod($obj, $method); + $ref_method->setAccessible(true); + $obj = (gettype($obj) === 'object') ? $obj : null; + + return function () use ($obj, $ref_method) { + $args = func_get_args(); + return $ref_method->invokeArgs($obj, $args); + }; + } + + protected static function getAccessibleRefProperty($obj, $property) + { + if (is_object($obj)) { + $ref_class = new ReflectionObject($obj); + } else { + $ref_class = new ReflectionClass($obj); + } + + $ref_property = $ref_class->getProperty($property); + $ref_property->setAccessible(true); + + return $ref_property; + } + + /** + * @param object|string $obj object or class name + * @param string $property property name + * @param mixed $value value + */ + public static function setPrivateProperty($obj, $property, $value) + { + $ref_property = self::getAccessibleRefProperty($obj, $property); + $ref_property->setValue($obj, $value); + } + + /** + * @param object|string $obj object or class name + * @param string $property property name + * @return mixed value + */ + public static function getPrivateProperty($obj, $property) + { + $ref_property = self::getAccessibleRefProperty($obj, $property); + return $ref_property->getValue($obj); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php new file mode 100644 index 00000000000..afa1ccab6b8 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRequest.php @@ -0,0 +1,366 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestRequest +{ + protected $testCase; + + /** + * @var CIPHPUnitTestSuperGlobal + */ + protected $superGlobal; + + /** + * @var CIPHPUnitTestRouter + */ + protected $router; + + /** + * @var callable[] callable called post controller constructor + */ + protected $callables = []; + + /** + * @var callable[] callable called pre controller constructor + */ + protected $callablePreConstructors = []; + + protected $enableHooks = false; + + /** + * @var CI_Hooks + */ + protected $hooks; + + public function __construct(PHPUnit_Framework_TestCase $testCase) + { + $this->testCase = $testCase; + $this->superGlobal = new CIPHPUnitTestSuperGlobal(); + $this->router = new CIPHPUnitTestRouter(); + } + + /** + * Set HTTP request header + * + * @param string $name header name + * @param string $value value + */ + public function setHeader($name, $value) + { + $this->superGlobal->set_SERVER_HttpHeader($name, $value); + } + + /** + * Set (and Reset) callable + * + * @param callable $callable function to run after controller instantiation + */ + public function setCallable(callable $callable) + { + $this->callables = []; + $this->callables[] = $callable; + } + + /** + * Add callable + * + * @param callable $callable function to run after controller instantiation + */ + public function addCallable(callable $callable) + { + $this->callables[] = $callable; + } + + /** + * Set (and Reset) callable pre constructor + * + * @param callable $callable function to run before controller instantiation + */ + public function setCallablePreConstructor(callable $callable) + { + $this->callablePreConstructors = []; + $this->callablePreConstructors[] = $callable; + } + + /** + * Add callable pre constructor + * + * @param callable $callable function to run before controller instantiation + */ + public function addCallablePreConstructor(callable $callable) + { + $this->callablePreConstructors[] = $callable; + } + + /** + * Enable Hooks for Controllres + * This enables only pre_controller, post_controller_constructor, post_controller + */ + public function enableHooks() + { + $this->enableHooks = true; + $this->hooks =& load_class('Hooks', 'core'); + } + + /** + * Request to Controller + * + * @param string $http_method HTTP method + * @param array|string $argv array of controller,method,arg|uri + * @param array|string $params POST params/GET params|raw_input_stream + */ + public function request($http_method, $argv, $params = []) + { + if (is_string($argv)) + { + $argv = ltrim($argv, '/'); + } + + // Set super globals + $_SERVER['REQUEST_METHOD'] = $http_method; + $this->superGlobal->set_GET($argv, $params); + $this->superGlobal->set_POST($params); + $this->superGlobal->set_SERVER_REQUEST_URI($argv); + + try { + if (is_array($argv)) + { + return $this->callControllerMethod( + $http_method, $argv, $params + ); + } + else + { + return $this->requestUri($http_method, $argv, $params); + } + } + // redirect() + catch (CIPHPUnitTestRedirectException $e) + { + if ($e->getCode() === 0) + { + set_status_header(200); + } + else + { + set_status_header($e->getCode()); + } + $CI =& get_instance(); + $CI->output->_status['redirect'] = $e->getMessage(); + } + // show_404() + catch (CIPHPUnitTestShow404Exception $e) + { + $this->processError($e); + return $e->getMessage(); + } + // show_error() + catch (CIPHPUnitTestShowErrorException $e) + { + $this->processError($e); + return $e->getMessage(); + } + } + + protected function processError(Exception $e) + { + set_status_header($e->getCode()); + } + + /** + * Call Controller Method + * + * @param string $http_method HTTP method + * @param array $argv controller, method [, arg1, ...] + * @param array|string $request_params POST params/GET params|raw_input_stream + */ + protected function callControllerMethod($http_method, $argv, $request_params) + { + $_SERVER['argv'] = array_merge(['index.php'], $argv); + + $class = ucfirst($argv[0]); + $method = $argv[1]; + + // Remove controller and method + array_shift($argv); + array_shift($argv); + +// $request = [ +// 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'], +// 'class' => $class, +// 'method' => $method, +// 'params' => $argv, +// '$_GET' => $_GET, +// '$_POST' => $_POST, +// ]; +// var_dump($request, $_SERVER['argv']); + + // Reset CodeIgniter instance state + reset_instance(); + + $this->setRawInputStream($request_params); + + // 404 checking + if (! class_exists($class) || ! method_exists($class, $method)) + { + // If 404, CodeIgniter instance is not created yet. So create it here + // Because we need CI->output->_status to store info + $CI =& get_instance(); + if ($CI instanceof CIPHPUnitTestNullCodeIgniter) + { + CIPHPUnitTest::createCodeIgniterInstance(); + } + + show_404($class.'::'.$method . '() is not found'); + } + + $params = $argv; + + return $this->createAndCallController($class, $method, $params, false); + } + + /** + * Request to URI + * + * @param string $http_method HTTP method + * @param string $uri URI string + * @param array|string $request_params POST params/GET params|raw_input_stream + */ + protected function requestUri($http_method, $uri, $request_params) + { + $_SERVER['argv'] = ['index.php', $uri]; + + // Force cli mode because if not, it changes URI (and RTR) behavior + $cli = is_cli(); + set_is_cli(TRUE); + + // Reset CodeIgniter instance state + reset_instance(); + + $this->setRawInputStream($request_params); + + // Get route + list($class, $method, $params) = $this->router->getRoute(); + + // Restore cli mode + set_is_cli($cli); + +// $request = [ +// 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'], +// 'class' => $class, +// 'method' => $method, +// 'params' => $params, +// '$_GET' => $_GET, +// '$_POST' => $_POST, +// ]; +// var_dump($request, $_SERVER['argv']); + + return $this->createAndCallController($class, $method, $params); + } + + protected function callHook($hook) + { + if ($this->enableHooks) + { + return $this->hooks->call_hook($hook); + } + + return false; + } + + protected function setRawInputStream($string) + { + if (is_string($string)) + { + $INPUT =& load_class('Input', 'core'); + CIPHPUnitTestReflection::setPrivateProperty( + $INPUT, + '_raw_input_stream', + $string + ); + } + } + + protected function createAndCallController($class, $method, $params, $call_display = true) + { + ob_start(); + + $this->callHook('pre_controller'); + + // Run callablePreConstructor + if ($this->callablePreConstructors !== []) + { + foreach ($this->callablePreConstructors as $callable) + { + $callable(); + } + } + + // Create controller + if (CIPHPUnitTest::wiredesignzHmvcInstalled()) + { + new CI(); + } + $controller = new $class; + $CI =& get_instance(); + + // Set CodeIgniter instance to TestCase + $this->testCase->setCI($CI); + + // Set default response code 200 + set_status_header(200); + // Run callable + if ($this->callables !== []) + { + foreach ($this->callables as $callable) + { + $callable($CI); + } + } + + $this->callHook('post_controller_constructor'); + + // Call controller method + call_user_func_array([$controller, $method], $params); + + $this->callHook('post_controller'); + + if ($call_display && $this->callHook('display_override') === false) + { + $CI->output->_display(); + } + + $output = ob_get_clean(); + + if ($output == '') + { + $output = $CI->output->get_output(); + } + + return $output; + } + + /** + * Get HTTP Status Code Info + * + * @return array ['code' => code, 'text' => text] + * @throws LogicException + */ + public function getStatus() + { + $CI =& get_instance(); + if (! isset($CI->output->_status)) + { + throw new LogicException('Status code is not set. You must call $this->request() first'); + } + + return $CI->output->_status; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRouter.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRouter.php new file mode 100644 index 00000000000..98bbbeebac3 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestRouter.php @@ -0,0 +1,76 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestRouter +{ + /** + * Get Route including 404 check + * + * @see core/CodeIgniter.php + * + * @return array [class, method, pararms] + */ + public function getRoute() + { + $RTR =& load_class('Router', 'core'); + $URI =& load_class('URI', 'core'); + + $e404 = FALSE; + $class = ucfirst($RTR->class); + $method = $RTR->method; + + if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php')) + { + $e404 = TRUE; + } + else + { + require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php'); + + if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method)) + { + $e404 = TRUE; + } + elseif (method_exists($class, '_remap')) + { + $params = array($method, array_slice($URI->rsegments, 2)); + $method = '_remap'; + } + // WARNING: It appears that there are issues with is_callable() even in PHP 5.2! + // Furthermore, there are bug reports and feature/change requests related to it + // that make it unreliable to use in this context. Please, DO NOT change this + // work-around until a better alternative is available. + elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE)) + { + $e404 = TRUE; + } + } + + if ($e404) + { + // If 404, CodeIgniter instance is not created yet. So create it here. + // Because we need CI->output->_status + $CI =& get_instance(); + if ($CI instanceof CIPHPUnitTestNullCodeIgniter) + { + CIPHPUnitTest::createCodeIgniterInstance(); + } + + show_404($RTR->directory.$class.'/'.$method.' is not found'); + } + + if ($method !== '_remap') + { + $params = array_slice($URI->rsegments, 2); + } + + return [$class, $method, $params]; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestSuperGlobal.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestSuperGlobal.php new file mode 100644 index 00000000000..c61c42dcdcb --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/CIPHPUnitTestSuperGlobal.php @@ -0,0 +1,124 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestSuperGlobal +{ + public static function set_Global($name, $value) + { + $GLOBALS[$name] = $value; + } + + public function set_POST($params) + { + if (is_array($params)) + { + if ($_SERVER['REQUEST_METHOD'] === 'POST') + { + $_POST = $params; + } + } + } + + public function set_GET(&$argv, $params) + { + if (is_string($argv)) + { + $query_string = $this->getQueryString($argv); + if ($query_string !== null) + { + // Set $_GET if URI string has query string + parse_str($query_string, $_GET); + // Remove query string from URI string + $argv = substr($argv, 0, -strlen($query_string)-1); + } + } + + if (is_array($params)) + { + if ($_SERVER['REQUEST_METHOD'] === 'GET') + { + // if GET params are passed, overwrite $_GET + if ($params !== []) + { + $_GET = $params; + } + } + } + } + + public function set_SERVER_REQUEST_URI($argv) + { + $path = ''; + if (is_string($argv)) + { + $path = $argv; + } + elseif (is_array($argv)) + { + // Generate URI path from array of controller, method, arg, ... + $path = implode('/', $argv); + } + + if ($_GET !== []) + { + $_SERVER['REQUEST_URI'] = + '/' . $path . '?' + . http_build_query($_GET); + } + else + { + $_SERVER['REQUEST_URI'] = '/' . $path; + } + } + + /** + * Parse URI string and Get query string + * + * @param string $uri + * @return string|null + * @throws LogicException + */ + protected function getQueryString($uri) + { + $query_string = parse_url('http://localhost/'.$uri, PHP_URL_QUERY); + + if ($query_string === false) + { + throw new LogicException('Bad URI string: ' . $uri); + } + + return $query_string; + } + + /** + * Set HTTP request header to $_SERVER + * + * @param string $name header name + * @param string $value value + */ + public function set_SERVER_HttpHeader($name, $value) + { + $normalized_name = str_replace('-', '_', strtoupper($name)); + + if ( + $normalized_name === 'CONTENT_LENGTH' + || $normalized_name === 'CONTENT_TYPE' + ) + { + $key = $normalized_name; + } + else + { + $key = 'HTTP_' . $normalized_name; + } + + $_SERVER[$key] = $value; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/TestSuiteProvider.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/TestSuiteProvider.php new file mode 100644 index 00000000000..18dd7f96419 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/TestSuiteProvider.php @@ -0,0 +1,122 @@ + + * @link https://github.com/kenjis/ci-phpunit-test + */ + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The MIT License + * + * Copyright 2015 Eric VILLARD . + * + * 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. + * + * @package netbeans\phpunit\support + * @author Eric VILLARD + * @copyright (c) 2015 Eric VILLARD + * @license http://opensource.org/licenses/MIT MIT License + */ + +namespace netbeans\phpunit\support; + +use PHPUnit_Framework_TestSuite; +use PHPUnit_Util_Configuration; + +/** + * TestSuiteProvider + * + * @package netbeans\phpunit\support + * @author Eric VILLARD + * @copyright (c) 2015 Eric VILLARD + * @license http://opensource.org/licenses/MIT MIT License + */ +final class TestSuiteProvider +{ + /** + * phpunit configuration file + * + * @var string + */ + private static $file; + + /** + * constructor + */ + private function __construct() {} + + /** + * set the phpunit configuration file + * + * @param string $file the path or filename of the phunit configuration file + */ + public static function setConfigurationFile($file) + { + static::$file = $file; + } + + /** + * get the phpunit test suite instance + * + * @return PHPUnit_Framework_TestSuite returns the phpunit test suite instance + * @throws FileNotFoundException if the file is not found + */ + public static function suite() + { + $file = static::checkConfigurationFile( + static::getConfigurationFile() + ); + + return PHPUnit_Util_Configuration::getInstance($file) + ->getTestSuiteConfiguration(); + } + + /** + * get the phpunit configuration file + * + * @return string + */ + private static function getConfigurationFile() + { + static::$file = isset(static::$file) + ? static::$file + : APPPATH.'tests/phpunit.xml'; + + return static::$file; + } + + /** + * check the given file + * + * @param string $file file to check + * @return string returns the file if it is valid + * @throws FileNotFoundException if the file is not found + */ + private static function checkConfigurationFile($file) + { + if (!file_exists($file)) { + throw new \RuntimeException("The requested phpunit configuration was not found at $file"); + } + + return $file; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/alias/MonkeyPatch.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/alias/MonkeyPatch.php new file mode 100644 index 00000000000..0a2ed7fc6d5 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/alias/MonkeyPatch.php @@ -0,0 +1,5 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +// Autoloader for ci-phpunit-test +require __DIR__ . '/CIPHPUnitTestAutoloader.php'; +require __DIR__ . '/CIPHPUnitTestFileCache.php'; +$cache = new CIPHPUnitTestFileCache( + __DIR__ . '/tmp/cache/autoload.php' +); +$autoload_dirs = CIPHPUnitTest::getAutoloadDirs(); +$autoloader = new CIPHPUnitTestAutoloader($cache, $autoload_dirs); +spl_autoload_register([$autoloader, 'load']); + +// Register CodeIgniter's tests/mocks/autoloader.php +define('SYSTEM_PATH', BASEPATH); +require APPPATH .'tests/mocks/autoloader.php'; +spl_autoload_register('autoload'); diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestExitException.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestExitException.php new file mode 100644 index 00000000000..353710b4452 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestExitException.php @@ -0,0 +1,18 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestExitException extends RuntimeException +{ + public $file; + public $line; + public $class; + public $method; + public $exit_status; +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestRedirectException.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestRedirectException.php new file mode 100644 index 00000000000..eaa639b46d0 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestRedirectException.php @@ -0,0 +1,13 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestRedirectException extends RuntimeException +{ +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestShow404Exception.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestShow404Exception.php new file mode 100644 index 00000000000..98d0997d5bc --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestShow404Exception.php @@ -0,0 +1,13 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestShow404Exception extends RuntimeException +{ +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestShowErrorException.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestShowErrorException.php new file mode 100644 index 00000000000..8aad5f967aa --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/exceptions/CIPHPUnitTestShowErrorException.php @@ -0,0 +1,13 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +class CIPHPUnitTestShowErrorException extends RuntimeException +{ +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/functions.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/functions.php new file mode 100644 index 00000000000..d258b846fc7 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/functions.php @@ -0,0 +1,99 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +/** + * Inject instance to load_class() function + * + * @param string $classname + * @param object $instance + */ +function load_class_instance($classname, $instance) +{ + load_class($classname, '', NULL, FALSE, $instance); +} + +/** + * Reset CodeIgniter instance + */ +function reset_instance() +{ + // Reset loaded classes + load_class('', '', NULL, TRUE); + is_loaded('', TRUE); + + // Reset config functions + reset_config(); + + // Close db connection + $CI =& get_instance(); + if (isset($CI->db)) + { + if ( + $CI->db->dsn !== 'sqlite::memory:' + && $CI->db->database !== ':memory:' + ) + { + $CI->db->close(); + $CI->db = null; + } + else + { + // Don't close if SQLite in-memory database + // If we close it, all tables and stored data will be gone + load_class_instance('db', $CI->db); + } + } + + // Load core classes + $BM =& load_class('Benchmark', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('BM', $BM); + $EXT =& load_class('Hooks', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('EXT', $EXT); + $CFG =& load_class('Config', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('CFG', $CFG); + $UNI =& load_class('URI', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('UNI', $UNI); +// $URI =& load_class('Utf8', 'core'); +// CIPHPUnitTestSuperGlobal::set_Global('URI', $URI); + $RTR =& load_class('Router', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('RTR', $RTR); + $OUT =& load_class('Output', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('OUT', $OUT); + $SEC =& load_class('Security', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('SEC', $SEC); + $IN =& load_class('Input', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('IN', $IN); + $LANG =& load_class('Lang', 'core'); + CIPHPUnitTestSuperGlobal::set_Global('LANG', $LANG); + + CIPHPUnitTest::loadLoader(); + + // Remove CodeIgniter instance + $CI = new CIPHPUnitTestNullCodeIgniter(); +} + +/** + * Set return value of is_cli() function + * + * @param bool $return + */ +function set_is_cli($return) +{ + is_cli($return); +} + +/** + * Reset config functions + */ +function reset_config() +{ + get_config([], TRUE); + config_item(NULL, TRUE); +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Cache.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Cache.php new file mode 100644 index 00000000000..7d5ee385eac --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Cache.php @@ -0,0 +1,282 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch; + +use RuntimeException; + +use RecursiveIteratorIterator; +use RecursiveDirectoryIterator; + +class Cache +{ + private static $project_root; + private static $cache_dir; + private static $src_cache_dir; + private static $tmp_function_blacklist_file; + private static $tmp_function_whitelist_file; + private static $tmp_patcher_list_file; + private static $tmp_include_paths_file; + private static $tmp_exclude_paths_file; + + public static function setProjectRootDir($dir) + { + self::$project_root = realpath($dir); + if (self::$project_root === false) + { + throw new LogicException("No such directory: $dir"); + } + } + + public static function setCacheDir($dir) + { + self::createDir($dir); + self::$cache_dir = realpath($dir); + + if (self::$cache_dir === false) + { + throw new LogicException("No such directory: $dir"); + } + + self::$src_cache_dir = self::$cache_dir . '/src'; + self::$tmp_function_whitelist_file = + self::$cache_dir . '/conf/func_whiltelist.php'; + self::$tmp_function_blacklist_file = + self::$cache_dir . '/conf/func_blacklist.php'; + self::$tmp_patcher_list_file = + self::$cache_dir . '/conf/patcher_list.php'; + self::$tmp_include_paths_file = + self::$cache_dir . '/conf/include_paths.php'; + self::$tmp_exclude_paths_file = + self::$cache_dir . '/conf/exclude_paths.php'; + } + + public static function getCacheDir() + { + return self::$cache_dir; + } + + public static function getSrcCacheFilePath($path) + { + $len = strlen(self::$project_root); + $relative_path = substr($path, $len); + + if ($relative_path === false) + { + return false; + } + + return self::$src_cache_dir . '/' . $relative_path; + } + + protected static function createDir($dir) + { + if (! is_dir($dir)) + { + if (! @mkdir($dir, 0777, true)) + { + throw new RuntimeException('Failed to create folder: ' . $dir); + } + } + } + + /** + * @param string $path original source file path + * @return string|false + */ + public static function getValidSrcCachePath($path) + { + $cache_file = self::getSrcCacheFilePath($path); + + if ( + is_readable($cache_file) && filemtime($cache_file) > filemtime($path) + ) + { + return $cache_file; + } + + return false; + } + + /** + * Write to src cache file + * + * @param string $path original source file path + * @param string $source source code + */ + public static function writeSrcCacheFile($path, $source) + { + $cache_file = self::getSrcCacheFilePath($path); + if ($cache_file !== false) + { + self::writeCacheFile($cache_file, $source); + } + } + + /** + * Write to cache file + * + * @param string $path file path + * @param string $contents file contents + */ + public static function writeCacheFile($path, $contents) + { + $dir = dirname($path); + self::createDir($dir); + file_put_contents($path, $contents); + } + + public static function getTmpFunctionBlacklistFile() + { + return self::$tmp_function_blacklist_file; + } + + public static function createTmpListDir() + { + if (is_readable(self::$tmp_function_blacklist_file)) + { + return; + } + + $dir = dirname(self::$tmp_function_blacklist_file); + self::createDir($dir); + + touch(self::$tmp_function_blacklist_file); + } + + public static function appendTmpFunctionBlacklist($function) + { + file_put_contents( + self::getTmpFunctionBlacklistFile(), $function . "\n", FILE_APPEND + ); + } + + protected static function writeTmpConfFile($filename, array $list) + { + $contents = implode("\n", $list); + file_put_contents( + self::$$filename, $contents + ); + } + + public static function writeTmpFunctionWhitelist(array $functions) + { + return self::writeTmpConfFile( + 'tmp_function_whitelist_file', $functions + ); + } + + public static function writeTmpPatcherList(array $patchers) + { + return self::writeTmpConfFile( + 'tmp_patcher_list_file', $patchers + ); + } + + public static function writeTmpIncludePaths(array $paths) + { + return self::writeTmpConfFile( + 'tmp_include_paths_file', $paths + ); + } + + public static function writeTmpExcludePaths(array $paths) + { + return self::writeTmpConfFile( + 'tmp_exclude_paths_file', $paths + ); + } + + protected static function getTmpConfFile($filename) + { + if (is_readable(self::$$filename)) + { + return file( + self::$$filename, + FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES + ); + } + return []; + } + + public static function getTmpFunctionWhitelist() + { + return self::getTmpConfFile('tmp_function_whitelist_file'); + } + + public static function getTmpPatcherList() + { + return self::getTmpConfFile('tmp_patcher_list_file'); + } + + public static function getTmpIncludePaths() + { + return self::getTmpConfFile('tmp_include_paths_file'); + } + + public static function getTmpExcludePaths() + { + return self::getTmpConfFile('tmp_exclude_paths_file'); + } + + /** + * @param string $orig_file original source file + * @return string removed cache file + */ + public static function removeSrcCacheFile($orig_file) + { + $cache = self::getSrcCacheFilePath($orig_file); + @unlink($cache); + MonkeyPatchManager::log('remove_src_cache: ' . $cache); + return $cache; + } + + public static function clearSrcCache() + { + self::recursiveUnlink(self::$src_cache_dir); + MonkeyPatchManager::log('clear_src_cache: cleared ' . self::$src_cache_dir); + } + + public static function clearCache() + { + self::recursiveUnlink(self::$cache_dir); + MonkeyPatchManager::log('clear_cache: cleared ' . self::$cache_dir); + } + + /** + * Recursive Unlink + * + * @param string $dir + */ + protected static function recursiveUnlink($dir) + { + if (! is_dir($dir)) + { + return; + } + + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( + $dir, RecursiveDirectoryIterator::SKIP_DOTS + ), + RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($iterator as $file) { + if ($file->isDir()) { + rmdir($file); + } else { + unlink($file); + } + } + + rmdir($dir); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Exception/ExitException.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Exception/ExitException.php new file mode 100644 index 00000000000..8d523a747bd --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Exception/ExitException.php @@ -0,0 +1,22 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Exception; + +use RuntimeException; + +class ExitException extends RuntimeException +{ + public $file; + public $line; + public $class; + public $method; + public $exit_status; +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/IncludeStream.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/IncludeStream.php new file mode 100644 index 00000000000..ec7e9859e75 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/IncludeStream.php @@ -0,0 +1,276 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +/** + * Copyright for Original Code + * + * @author Ignas Rudaitis + * @copyright 2010-2015 Ignas Rudaitis + * @license http://www.opensource.org/licenses/mit-license.html + * @link http://antecedent.github.com/patchwork + * + * @see https://github.com/antecedent/patchwork/blob/1.3.5/src/Preprocessor/Stream.php + */ + +namespace Kenjis\MonkeyPatch; + +class IncludeStream +{ + const STREAM_OPEN_FOR_INCLUDE = 128; + const STAT_MTIME_NUMERIC_OFFSET = 9; + const STAT_MTIME_ASSOC_OFFSET = 'mtime'; + + protected static $protocols = array('file', 'phar'); + + public $context; + public $resource; + + public static function wrap() + { + foreach (static::$protocols as $protocol) { + stream_wrapper_unregister($protocol); + stream_wrapper_register($protocol, get_called_class()); + } + } + + public static function unwrap() + { + foreach (static::$protocols as $protocol) { + stream_wrapper_restore($protocol); + } + } + + protected function shouldPreprocess($path) + { + return PathChecker::check($path); + } + + protected function preprocessAndOpen($path) + { + return MonkeyPatchManager::patch($path); + } + + public function stream_open($path, $mode, $options, &$openedPath) + { + $this->unwrap(); + + MonkeyPatchManager::log('stream_open: ' . $path); + + $including = (bool) ($options & self::STREAM_OPEN_FOR_INCLUDE); + if ($including && $this->shouldPreprocess($path)) { + $this->resource = $this->preprocessAndOpen($path); + $this->wrap(); + return true; + } + if (isset($this->context)) { + $this->resource = fopen($path, $mode, $options, $this->context); + } else { + $this->resource = fopen($path, $mode, $options); + } + $this->wrap(); + return $this->resource !== false; + } + + public function stream_close() + { + return fclose($this->resource); + } + + public function stream_eof() + { + return feof($this->resource); + } + + public function stream_flush() + { + return fflush($this->resource); + } + + public function stream_read($count) + { + return fread($this->resource, $count); + } + + public function stream_seek($offset, $whence = SEEK_SET) + { + return fseek($this->resource, $offset, $whence) === 0; + } + + public function stream_stat() + { + $result = fstat($this->resource); + if ($result) { + $result[self::STAT_MTIME_ASSOC_OFFSET]++; + $result[self::STAT_MTIME_NUMERIC_OFFSET]++; + } + return $result; + } + + public function stream_tell() + { + return ftell($this->resource); + } + + public function url_stat($path, $flags) + { + $this->unwrap(); + if ($flags & STREAM_URL_STAT_QUIET) { + set_error_handler(function() {}); + } + $result = stat($path); + if ($flags & STREAM_URL_STAT_QUIET) { + restore_error_handler(); + } + $this->wrap(); + if ($result) { + $result[self::STAT_MTIME_ASSOC_OFFSET]++; + $result[self::STAT_MTIME_NUMERIC_OFFSET]++; + } + return $result; + } + + public function dir_closedir() + { + closedir($this->resource); + return true; + } + + public function dir_opendir($path, $options) + { + $this->unwrap(); + if (isset($this->context)) { + $this->resource = opendir($path, $this->context); + } else { + $this->resource = opendir($path); + } + $this->wrap(); + return $this->resource !== false; + } + + public function dir_readdir() + { + return readdir($this->resource); + } + + public function dir_rewinddir() + { + rewinddir($this->resource); + return true; + } + + public function mkdir($path, $mode, $options) + { + $this->unwrap(); + if (isset($this->context)) { + $result = mkdir($path, $mode, $options, $this->context); + } else { + $result = mkdir($path, $mode, $options); + } + $this->wrap(); + return $result; + } + + public function rename($path_from, $path_to) + { + $this->unwrap(); + if (isset($this->context)) { + $result = rename($path_from, $path_to, $this->context); + } else { + $result = rename($path_from, $path_to); + } + $this->wrap(); + return $result; + } + + public function rmdir($path, $options) + { + $this->unwrap(); + if (isset($this->context)) { + $result = rmdir($path, $this->context); + } else { + $result = rmdir($path); + } + $this->wrap(); + return $result; + } + + public function stream_cast($cast_as) + { + return $this->resource; + } + + public function stream_lock($operation) + { + return flock($this->resource, $operation); + } + + public function stream_set_option($option, $arg1, $arg2) + { + switch ($option) { + case STREAM_OPTION_BLOCKING: + return stream_set_blocking($this->resource, $arg1); + case STREAM_OPTION_READ_TIMEOUT: + return stream_set_timeout($this->resource, $arg1, $arg2); + case STREAM_OPTION_WRITE_BUFFER: + return stream_set_write_buffer($this->resource, $arg1); + case STREAM_OPTION_READ_BUFFER: + return stream_set_read_buffer($this->resource, $arg1); + } + } + + public function stream_write($data) + { + return fwrite($this->resource, $data); + } + + public function unlink($path) + { + $this->unwrap(); + if (isset($this->context)) { + $result = unlink($path, $this->context); + } else { + $result = unlink($path); + } + $this->wrap(); + return $result; + } + + public function stream_metadata($path, $option, $value) + { + $this->unwrap(); + switch ($option) { + case STREAM_META_TOUCH: + if (empty($value)) { + $result = touch($path); + } else { + $result = touch($path, $value[0], $value[1]); + } + break; + case STREAM_META_OWNER_NAME: + case STREAM_META_OWNER: + $result = chown($path, $value); + break; + case STREAM_META_GROUP_NAME: + case STREAM_META_GROUP: + $result = chgrp($path, $value); + break; + case STREAM_META_ACCESS: + $result = chmod($path, $value); + break; + } + $this->wrap(); + return $result; + } + + public function stream_truncate($new_size) + { + return ftruncate($this->resource, $new_size); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/InvocationVerifier.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/InvocationVerifier.php new file mode 100644 index 00000000000..2e375969aed --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/InvocationVerifier.php @@ -0,0 +1,79 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch; + +use PHPUnit_Framework_TestCase; + +class InvocationVerifier +{ + public static function verify(array $expected_invocations, array $invocations) + { + if ($expected_invocations === []) + { + return; + } + + foreach ($expected_invocations as $class_method => $data) + { + foreach ($data as $params_times) + { + list($expected_params, $expected_times) = $params_times; + + $invoked = isset($invocations[$class_method]); + if ($invoked === false) + { + $actual_times = 0; + } + elseif ($expected_params === null) + { + $actual_times = count($invocations[$class_method]); + } + else + { + $count = 0; + foreach ($invocations[$class_method] as $actual_params) + { + if ($actual_params == $expected_params) + { + $count++; + } + } + $actual_times = $count; + } + + if ($expected_times === 0) + { + PHPUnit_Framework_TestCase::assertEquals( + $expected_times, + $actual_times, + $class_method . '() expected to be not invoked, but invoked ' . $actual_times . ' times.' + ); + } + elseif ($expected_times === '+') + { + PHPUnit_Framework_TestCase::assertGreaterThanOrEqual( + 1, + $actual_times, + $class_method . '() expected to be invoked at least one time, but invoked ' . $actual_times . ' times.' + ); + } + else + { + PHPUnit_Framework_TestCase::assertEquals( + $expected_times, + $actual_times, + $class_method . '() expected to be invoked ' . $expected_times . ' times, but invoked ' . $actual_times . ' times.' + ); + } + } + } + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/MonkeyPatch.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/MonkeyPatch.php new file mode 100644 index 00000000000..7e6ea488dce --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/MonkeyPatch.php @@ -0,0 +1,156 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch; + +use Kenjis\MonkeyPatch\Patcher\FunctionPatcher\Proxy; +use Kenjis\MonkeyPatch\Patcher\ConstantPatcher\Proxy as ConstProxy; +use Kenjis\MonkeyPatch\Patcher\MethodPatcher\PatchManager; + +class MonkeyPatch +{ + /** + * Patch on function + * + * @param string $function function name + * @param mixed $return_value return value + * @param string $class_name class::method to apply this patch + */ + public static function patchFunction($function, $return_value, $class_method = null) + { + Proxy::patch__($function, $return_value, $class_method); + } + + /** + * Reset all patched fuctions + */ + public static function resetFunctions() + { + Proxy::reset__(); + } + + /** + * Patch on constant + * + * @param type $constant + * @param type $value + * @param type $class_method + */ + public static function patchConstant($constant, $value, $class_method = null) + { + ConstProxy::patch($constant, $value, $class_method); + } + + /** + * Reset all patched constants + */ + public static function resetConstants() + { + ConstProxy::reset(); + } + + /** + * Patch on class method + * + * @param string $class + * @param array $params [method_name => return_value] + */ + public static function patchMethod($class, $params) + { + PatchManager::set($class, $params); + } + + /** + * Reset all patched class method + */ + public static function resetMethods() + { + PatchManager::clear(); + } + + protected static function getClassname($class_method) + { + if (strpos($class_method, '::') === false) + { + return 'Kenjis\MonkeyPatch\Patcher\FunctionPatcher\Proxy'; + } + else + { + return 'Kenjis\MonkeyPatch\Patcher\MethodPatcher\PatchManager'; + } + } + + /** + * @param string $class_method class::method or function name + * @param int $times times + * @param array $params parameters + */ + public static function verifyInvokedMultipleTimes( + $class_method, $times, array $params = null + ) + { + $classname = self::getClassname($class_method); + $classname::setExpectedInvocations( + $class_method, $times, $params + ); + } + + /** + * @param string $class_method class::method or function name + * @param array $params parameters + */ + public static function verifyInvoked($class_method, array $params = null) + { + $classname = self::getClassname($class_method); + $classname::setExpectedInvocations( + $class_method, '+', $params + ); + } + + /** + * @param string $class_method class::method or function name + * @param array $params parameters + */ + public static function verifyInvokedOnce($class_method, array $params = null) + { + $classname = self::getClassname($class_method); + $classname::setExpectedInvocations( + $class_method, 1, $params + ); + } + + /** + * @param string $class_method class::method or function name + * @param array $params parameters + */ + public static function verifyNeverInvoked($class_method, array $params = null) + { + $classname = self::getClassname($class_method); + $classname::setExpectedInvocations( + $class_method, 0, $params + ); + } + + /** + * Run function verifcations + */ + public static function verifyFunctionInvocations() + { + Proxy::verifyInvocations(); + } + + /** + * Run method verifcations + */ + public static function verifyMethodInvocations() + { + PatchManager::verifyInvocations(); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/MonkeyPatchManager.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/MonkeyPatchManager.php new file mode 100644 index 00000000000..f2d57d0dd57 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/MonkeyPatchManager.php @@ -0,0 +1,322 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch; + +use LogicException; +use RuntimeException; +use PhpParser\ParserFactory; +use Kenjis\MonkeyPatch\Patcher\FunctionPatcher; + +class MonkeyPatchManager +{ + public static $debug = false; + + private static $php_parser = ParserFactory::PREFER_PHP5; + + private static $log_file; + private static $load_patchers = false; + private static $exit_exception_classname = + 'Kenjis\MonkeyPatch\Exception\ExitException'; + /** + * @var array list of patcher classname + */ + private static $patcher_list = [ + 'ExitPatcher', + 'FunctionPatcher', + 'MethodPatcher', + 'ConstantPatcher', + ]; + + public static function log($message) + { + if (! self::$debug) + { + return; + } + + $time = date('Y-m-d H:i:s'); + list($usec, $sec) = explode(' ', microtime()); + $usec = substr($usec, 1); + $log = "[$time $usec] $message\n"; + file_put_contents(self::$log_file, $log, FILE_APPEND); + } + + public static function setExitExceptionClassname($name) + { + self::$exit_exception_classname = $name; + } + + public static function getExitExceptionClassname() + { + return self::$exit_exception_classname; + } + + public static function getPhpParser() + { + return self::$php_parser; + } + + protected static function setDebug(array $config) + { + if (isset($config['debug'])) + { + self::$debug = $config['debug']; + } + if (self::$debug) + { + self::$log_file = __DIR__ . '/debug.log'; + } + } + + protected static function setDir(array $config) + { + if (isset($config['root_dir'])) + { + Cache::setProjectRootDir($config['root_dir']); + } + else + { + // APPPATH is constant in CodeIgniter + Cache::setProjectRootDir(APPPATH . '../'); + } + + if (! isset($config['cache_dir'])) + { + throw new LogicException('You have to set "cache_dir"'); + } + self::setCacheDir($config['cache_dir']); + } + + protected static function setPaths(array $config) + { + if (! isset($config['include_paths'])) + { + throw new LogicException('You have to set "include_paths"'); + } + self::setIncludePaths($config['include_paths']); + + if (isset($config['exclude_paths'])) + { + self::setExcludePaths($config['exclude_paths']); + } + } + + public static function init(array $config) + { + self::setDebug($config); + + if (isset($config['php_parser'])) + { + self::$php_parser = constant('PhpParser\ParserFactory::'.$config['php_parser']); + } + + self::setDir($config); + self::setPaths($config); + + Cache::createTmpListDir(); + + if (isset($config['patcher_list'])) + { + self::setPatcherList($config['patcher_list']); + } + self::checkPatcherListUpdate(); + self::checkPathsUpdate(); + + self::loadPatchers(); + + self::addTmpFunctionBlacklist(); + + if (isset($config['functions_to_patch'])) + { + FunctionPatcher::addWhitelists($config['functions_to_patch']); + } + self::checkFunctionWhitelistUpdate(); + FunctionPatcher::lockFunctionList(); + + if (isset($config['exit_exception_classname'])) + { + self::setExitExceptionClassname($config['exit_exception_classname']); + } + + // Register include stream wrapper for monkey patching + self::wrap(); + } + + protected static function checkPathsUpdate() + { + $cached = Cache::getTmpIncludePaths(); + $current = PathChecker::getIncludePaths(); + + // Updated? + if ($cached !== $current) + { + MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); + Cache::clearSrcCache(); + Cache::writeTmpIncludePaths($current); + } + + $cached = Cache::getTmpExcludePaths(); + $current = PathChecker::getExcludePaths(); + + // Updated? + if ($cached !== $current) + { + MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); + Cache::clearSrcCache(); + Cache::writeTmpExcludePaths($current); + } + } + + protected static function checkPatcherListUpdate() + { + $cached = Cache::getTmpPatcherList(); + + // Updated? + if ($cached !== self::$patcher_list) + { + MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); + Cache::clearSrcCache(); + Cache::writeTmpPatcherList(self::$patcher_list); + } + } + + protected static function checkFunctionWhitelistUpdate() + { + $cached = Cache::getTmpFunctionWhitelist(); + $current = FunctionPatcher::getFunctionWhitelist(); + + // Updated? + if ($cached !== $current) + { + MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); + Cache::clearSrcCache(); + Cache::writeTmpFunctionWhitelist($current); + } + } + + protected static function addTmpFunctionBlacklist() + { + $list = file(Cache::getTmpFunctionBlacklistFile()); + foreach ($list as $function) + { + FunctionPatcher::addBlacklist(trim($function)); + } + } + + public static function isEnabled($patcher) + { + return in_array($patcher, self::$patcher_list); + } + + public static function setPatcherList(array $list) + { + if (self::$load_patchers) + { + throw new LogicException("Can't change patcher list after initialisation"); + } + + self::$patcher_list = $list; + } + + public static function setCacheDir($dir) + { + Cache::setCacheDir($dir); + } + + public static function setIncludePaths(array $dir_list) + { + PathChecker::setIncludePaths($dir_list); + } + + public static function setExcludePaths(array $dir_list) + { + PathChecker::setExcludePaths($dir_list); + } + + public static function wrap() + { + IncludeStream::wrap(); + } + + public static function unwrap() + { + IncludeStream::unwrap(); + } + + /** + * @param string $path original source file path + * @return resource + * @throws LogicException + */ + public static function patch($path) + { + if (! is_readable($path)) + { + throw new LogicException("Can't read '$path'"); + } + + // Check cache file + if ($cache_file = Cache::getValidSrcCachePath($path)) + { + self::log('cache_hit: ' . $path); + return fopen($cache_file, 'r'); + } + + self::log('cache_miss: ' . $path); + $source = file_get_contents($path); + + list($new_source, $patched) = self::execPatchers($source); + + // Write to cache file + self::log('write_cache: ' . $path); + Cache::writeSrcCacheFile($path, $new_source); + + $resource = fopen('php://memory', 'rb+'); + fwrite($resource, $new_source); + rewind($resource); + return $resource; + } + + protected static function loadPatchers() + { + if (self::$load_patchers) + { + return; + } + + require __DIR__ . '/Patcher/AbstractPatcher.php'; + require __DIR__ . '/Patcher/Backtrace.php'; + + foreach (self::$patcher_list as $classname) + { + require __DIR__ . '/Patcher/' . $classname . '.php'; + } + + self::$load_patchers = true; + } + + protected static function execPatchers($source) + { + $patched = false; + foreach (self::$patcher_list as $classname) + { + $classname = 'Kenjis\MonkeyPatch\Patcher\\' . $classname; + $patcher = new $classname; + list($source, $patched_this) = $patcher->patch($source); + $patched = $patched || $patched_this; + } + + return [ + $source, + $patched, + ]; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/AbstractPatcher.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/AbstractPatcher.php new file mode 100644 index 00000000000..75acca42b81 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/AbstractPatcher.php @@ -0,0 +1,57 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher; + +use PhpParser\ParserFactory; +use PhpParser\Lexer; +use PhpParser\NodeTraverser; +use Kenjis\MonkeyPatch\MonkeyPatchManager; + +abstract class AbstractPatcher +{ + protected $node_visitor; + + public static $replacement; + + public function patch($source) + { + $patched = false; + static::$replacement = []; + + $parser = (new ParserFactory) + ->create( + MonkeyPatchManager::getPhpParser(), + new Lexer( + ['usedAttributes' => ['startTokenPos', 'endTokenPos']] + ) + ); + $traverser = new NodeTraverser; + $traverser->addVisitor($this->node_visitor); + + $ast_orig = $parser->parse($source); + $traverser->traverse($ast_orig); + + if (static::$replacement !== []) + { + $patched = true; + $new_source = static::generateNewSource($source); + } + else + { + $new_source = $source; + } + + return [ + $new_source, + $patched, + ]; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/Backtrace.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/Backtrace.php new file mode 100644 index 00000000000..547c6ced8b7 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/Backtrace.php @@ -0,0 +1,79 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher; + +use LogicException; + +class Backtrace +{ + private static $map = [ + 'FunctionPatcher' => 1, + 'MethodPatcher' => 0, + 'ConstantPatcher' => 0, + ]; + + public static function getInfo($patcher, $backtrace) + { + if (! isset(self::$map[$patcher])) + { + throw new LogicException("No such a patcher: $patcher"); + } + $offset = self::$map[$patcher]; + + // Supports PHP7 optimization + if (version_compare(PHP_VERSION, '6.0.0', '>')) + { + if ($backtrace[$offset]['function'] === '__callStatic') + { + $offset--; + } + } + + $file = isset($backtrace[$offset]['file']) + ? $backtrace[$offset]['file'] : null; + $line = isset($backtrace[$offset]['line']) + ? $backtrace[$offset]['line'] : null; + + if (isset($backtrace[$offset+2])) + { + $class = isset($backtrace[$offset+2]['class']) + ? $backtrace[$offset+2]['class'] + : null; + $function = $backtrace[$offset+2]['function']; + } + else + { + $class = null; + $function = null; + } + + if (isset($class)) + { + $method = $function; + $class_method = $class . '::' . $function; + $function = null; + } + else + { + $method = null; + $class_method = null; + } + + return [ + 'file' => $file, + 'line' => $line, + 'class' => $class, + 'method' => $method, + 'class_method' => $class_method, + 'function' => $function, + ]; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher.php new file mode 100644 index 00000000000..ba73e1d6676 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher.php @@ -0,0 +1,88 @@ + + * @license MIT License + * @copyright 2016 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher; + +require __DIR__ . '/ConstantPatcher/NodeVisitor.php'; +require __DIR__ . '/ConstantPatcher/Proxy.php'; + +use LogicException; + +use Kenjis\MonkeyPatch\Patcher\ConstantPatcher\NodeVisitor; + +class ConstantPatcher extends AbstractPatcher +{ + /** + * @var special constant names which we don't patch + */ + private static $blacklist = [ + 'true', + 'false', + 'null', + ]; + + public static $replacement; + + public function __construct() + { + $this->node_visitor = new NodeVisitor(); + } + + /** + * @param string $name constant name + * @return boolean + */ + public static function isBlacklisted($name) + { + if (in_array(strtolower($name), self::$blacklist)) + { + return true; + } + + return false; + } + + protected static function generateNewSource($source) + { + $tokens = token_get_all($source); + $new_source = ''; + $i = -1; + + ksort(self::$replacement); + reset(self::$replacement); + $replacement = each(self::$replacement); + + foreach ($tokens as $token) + { + $i++; + + if (is_string($token)) + { + $new_source .= $token; + } + elseif ($i == $replacement['key']) + { + $new_source .= $replacement['value']; + $replacement = each(self::$replacement); + } + else + { + $new_source .= $token[1]; + } + } + + if ($replacement !== false) + { + throw new LogicException('Replacement data still remain'); + } + + return $new_source; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher/NodeVisitor.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher/NodeVisitor.php new file mode 100644 index 00000000000..d6280206068 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher/NodeVisitor.php @@ -0,0 +1,126 @@ + + * @license MIT License + * @copyright 2016 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher\ConstantPatcher; + +use PhpParser\Node; +use PhpParser\Node\Expr\ConstFetch; +use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\NodeVisitorAbstract; + +use Kenjis\MonkeyPatch\Patcher\ConstantPatcher; + +class NodeVisitor extends NodeVisitorAbstract +{ + private $disable_const_rewrite_level = 0; + + public function enterNode(Node $node) + { + $callback = [$this, 'before' . ucfirst($node->getType())]; + if (is_callable($callback)) { + call_user_func_array($callback, [$node]); + } + } + + public function leaveNode(Node $node) + { + if (! ($node instanceof ConstFetch)) + { + $callback = [$this, 'rewrite' . ucfirst($node->getType())]; + if (is_callable($callback)) { + call_user_func_array($callback, [$node]); + } + + return; + } + + if ($this->disable_const_rewrite_level > 0) + { + return; + } + + if (! ($node->name instanceof Name)) + { + return; + } + + if (! $node->name->isUnqualified()) + { + return; + } + + if (! ConstantPatcher::isBlacklisted((string) $node->name)) + { + $replacement = new FullyQualified(array()); + $replacement->set( + '\__ConstProxy__::get(\'' . (string) $node->name . '\')' + ); + + $pos = $node->getAttribute('startTokenPos'); + ConstantPatcher::$replacement[$pos] = + '\__ConstProxy__::get(\'' . (string) $node->name .'\')'; + + $node->name = $replacement; + } + } + + /** + * The following logic is from: + * + * Thank you. + * + * The MIT License (MIT) + * Copyright (c) 2016 Badoo Development + */ + // Cannot rewrite constants that are used as default values in function arguments + public function beforeParam() + { + $this->disable_const_rewrite_level++; + } + + public function rewriteParam() + { + $this->disable_const_rewrite_level--; + } + + // Cannot rewrite constants that are used as default values in constant declarations + public function beforeConst() + { + $this->disable_const_rewrite_level++; + } + + public function rewriteConst() + { + $this->disable_const_rewrite_level--; + } + + // Cannot rewrite constants that are used as default values in property declarations + public function beforeStmt_PropertyProperty() + { + $this->disable_const_rewrite_level++; + } + + public function rewriteStmt_PropertyProperty() + { + $this->disable_const_rewrite_level--; + } + + // Cannot rewrite constants that are used as default values in static variable declarations + public function beforeStmt_StaticVar() + { + $this->disable_const_rewrite_level++; + } + + public function rewriteStmt_StaticVar() + { + $this->disable_const_rewrite_level--; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher/Proxy.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher/Proxy.php new file mode 100644 index 00000000000..3d956b203d1 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ConstantPatcher/Proxy.php @@ -0,0 +1,131 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher\ConstantPatcher; + +class_alias('Kenjis\MonkeyPatch\Patcher\ConstantPatcher\Proxy', '__ConstProxy__'); + +use LogicException; +use ReflectionConstant; +use ReflectionException; + +use Kenjis\MonkeyPatch\Patcher\ConstantPatcher; +use Kenjis\MonkeyPatch\Patcher\Backtrace; +use Kenjis\MonkeyPatch\MonkeyPatchManager; +use Kenjis\MonkeyPatch\Cache; +use Kenjis\MonkeyPatch\InvocationVerifier; + +class Proxy +{ + private static $patches = []; + private static $patches_to_apply = []; + + /** + * Set a constant patch + * + * @param string $constant constant name + * @param mixed $value value + * @param string $class_method class::method to apply this patch + * + * @throws LogicException + */ + public static function patch($constant, $value, $class_method = null) + { + self::$patches[$constant] = $value; + self::$patches_to_apply[$constant] = strtolower($class_method); + } + + /** + * Clear all patches and invocation data + */ + public static function reset() + { + self::$patches = []; + self::$patches_to_apply = []; + } + + protected static function logInvocation($constant) + { + if (MonkeyPatchManager::$debug) + { + $trace = debug_backtrace(); + $info = Backtrace::getInfo('ConstantPatcher', $trace); + + $file = $info['file']; + $line = $info['line']; + $method = isset($info['class_method']) ? $info['class_method'] : $info['function']; + + MonkeyPatchManager::log( + 'invoke_const: ' . $constant . ') on line ' . $line . ' in ' . $file . ' by ' . $method . '()' + ); + } + } + + protected static function checkCalledMethod($constant) + { + $trace = debug_backtrace(); + $info = Backtrace::getInfo('ConstantPatcher', $trace); + + $class = strtolower($info['class']); + $class_method = strtolower($info['class_method']); + + // Patches the constants only in the class + if (strpos(self::$patches_to_apply[$constant], '::') === false) + { + if (self::$patches_to_apply[$constant] !== $class) + { + return false; + } + return true; + } + //Patches the constants only in the class method + else + { + if (self::$patches_to_apply[$constant] !== $class_method) + { + return false; + } + return true; + } + } + + /** + * Get patched constant value + * + * @param string $constant + * @return mixed + */ + public static function get($constant) + { + self::logInvocation($constant); + + if (isset(self::$patches_to_apply[$constant])) + { + if (! self::checkCalledMethod($constant)) + { + MonkeyPatchManager::log( + 'invoke_const: ' . $constant . ' not patched (out of scope)' + ); + return constant($constant); + } + } + + if (array_key_exists($constant, self::$patches)) + { + MonkeyPatchManager::log('invoke_const: ' . $constant . ' patched'); + return self::$patches[$constant]; + } + + MonkeyPatchManager::log( + 'invoke_const: ' . $constant . ' not patched (no patch)' + ); + return constant($constant); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ExitPatcher.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ExitPatcher.php new file mode 100644 index 00000000000..83aae345399 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/ExitPatcher.php @@ -0,0 +1,54 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher; + +require __DIR__ . '/../Exception/ExitException.php'; + +class ExitPatcher +{ + public static function patch($source) + { + $tokens = token_get_all($source); + + $patched = false; + $new_source = ''; + $i = -1; + + foreach ($tokens as $token) { + $i++; + if (is_string($token)) + { + $new_source .= $token; + } + elseif ($token[0] === T_EXIT) + { + if ($tokens[$i+1] === ';') + { + $new_source .= 'exit__()'; + } + else + { + $new_source .= 'exit__'; + } + $patched = true; + } + else + { + $new_source .= $token[1]; + } + } + + return [ + $new_source, + $patched, + ]; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher.php new file mode 100644 index 00000000000..026039f4c78 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher.php @@ -0,0 +1,197 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher; + +require __DIR__ . '/FunctionPatcher/NodeVisitor.php'; +require __DIR__ . '/FunctionPatcher/Proxy.php'; + +use LogicException; + +use Kenjis\MonkeyPatch\Patcher\FunctionPatcher\NodeVisitor; + +class FunctionPatcher extends AbstractPatcher +{ + private static $lock_function_list = false; + + /** + * @var array list of function names (in lower case) which you patch + */ + private static $whitelist = [ + 'mt_rand', + 'rand', + 'uniqid', + 'hash_hmac', + 'md5', + 'sha1', + 'hash', + 'time', + 'microtime', + 'date', + 'function_exists', + 'header', + 'setcookie', + // Functions that have param called by reference + // Need to prepare method in FunctionPatcher\Proxy class + 'openssl_random_pseudo_bytes', + ]; + + /** + * @var array list of function names (in lower case) which can't be patched + */ + private static $blacklist = [ + // Segmentation fault + 'call_user_func_array', + 'exit__', + // Error: Only variables should be assigned by reference + 'get_instance', + 'get_config', + 'load_class', + 'get_mimes', + '_get_validation_object', + // has reference param + 'preg_replace', + 'preg_match', + 'preg_match_all', + 'array_unshift', + 'array_shift', + 'sscanf', + 'ksort', + 'krsort', + 'str_ireplace', + 'str_replace', + 'is_callable', + 'flock', + 'end', + 'idn_to_ascii', + // Special functions for ci-phpunit-test + 'show_404', + 'show_error', + 'redirect', + ]; + + public static $replacement; + + public function __construct() + { + $this->node_visitor = new NodeVisitor(); + } + + protected static function checkLock($error_msg) + { + if (self::$lock_function_list) + { + throw new LogicException($error_msg); + } + } + + public static function addWhitelists(array $function_list) + { + self::checkLock("You can't add to whitelist after initialization"); + + foreach ($function_list as $function_name) + { + self::$whitelist[] = strtolower($function_name); + } + } + + /** + * @return array + */ + public static function getFunctionWhitelist() + { + return self::$whitelist; + } + + public static function addBlacklist($function_name) + { + self::checkLock("You can't add to blacklist after initialization"); + + self::$blacklist[] = strtolower($function_name); + } + + public static function removeBlacklist($function_name) + { + self::checkLock("You can't remove from blacklist after initialization"); + + $key = array_search(strtolower($function_name), self::$blacklist); + array_splice(self::$blacklist, $key, 1); + } + + public static function lockFunctionList() + { + self::$lock_function_list = true; + } + + /** + * @param string $name function name + * @return boolean + */ + public static function isWhitelisted($name) + { + if (in_array(strtolower($name), self::$whitelist)) + { + return true; + } + + return false; + } + + /** + * @param string $name function name + * @return boolean + */ + public static function isBlacklisted($name) + { + if (in_array(strtolower($name), self::$blacklist)) + { + return true; + } + + return false; + } + + protected static function generateNewSource($source) + { + $tokens = token_get_all($source); + $new_source = ''; + $i = -1; + + ksort(self::$replacement); + reset(self::$replacement); + $replacement = each(self::$replacement); + + foreach ($tokens as $token) + { + $i++; + + if (is_string($token)) + { + $new_source .= $token; + } + elseif ($i == $replacement['key']) + { + $new_source .= $replacement['value']; + $replacement = each(self::$replacement); + } + else + { + $new_source .= $token[1]; + } + } + + if ($replacement !== false) + { + throw new LogicException('Replacement data still remain'); + } + + return $new_source; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher/NodeVisitor.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher/NodeVisitor.php new file mode 100644 index 00000000000..b97bfe9b2e6 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher/NodeVisitor.php @@ -0,0 +1,70 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +/** + * Copyright for Original Code + * + * @author Adrian Philipp + * @copyright 2014 Adrian Philipp + * @license https://github.com/adri/monkey/blob/dfbb93ae09a2c0712f43eab7ced76d3f49989fbe/LICENSE + * @link https://github.com/adri/monkey + * + * @see https://github.com/adri/monkey/blob/dfbb93ae09a2c0712f43eab7ced76d3f49989fbe/testTest.php + */ + +namespace Kenjis\MonkeyPatch\Patcher\FunctionPatcher; + +use ReflectionFunction; +use ReflectionException; + +use PhpParser\Node; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\NodeVisitorAbstract; + +use Kenjis\MonkeyPatch\Patcher\FunctionPatcher; + +class NodeVisitor extends NodeVisitorAbstract +{ + public function leaveNode(Node $node) + { + if (! ($node instanceof FuncCall)) + { + return; + } + + if (! ($node->name instanceof Name)) + { + return; + } + + if (! $node->name->isUnqualified()) + { + return; + } + + if ( + FunctionPatcher::isWhitelisted((string) $node->name) + && ! FunctionPatcher::isBlacklisted((string) $node->name) + ) { + $replacement = new FullyQualified(array()); + $replacement->set( + '\__FuncProxy__::' . (string) $node->name + ); + + $pos = $node->getAttribute('startTokenPos'); + FunctionPatcher::$replacement[$pos] = + '\__FuncProxy__::' . (string) $node->name; + + $node->name = $replacement; + } + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher/Proxy.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher/Proxy.php new file mode 100644 index 00000000000..5b75bdd80e9 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher/Proxy.php @@ -0,0 +1,282 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher\FunctionPatcher; + +class_alias('Kenjis\MonkeyPatch\Patcher\FunctionPatcher\Proxy', '__FuncProxy__'); + +use LogicException; +use ReflectionFunction; +use ReflectionException; + +use Kenjis\MonkeyPatch\Patcher\FunctionPatcher; +use Kenjis\MonkeyPatch\Patcher\Backtrace; +use Kenjis\MonkeyPatch\MonkeyPatchManager; +use Kenjis\MonkeyPatch\Cache; +use Kenjis\MonkeyPatch\InvocationVerifier; + +class Proxy +{ + private static $patches = []; + private static $patches_to_apply = []; + private static $expected_invocations = []; + private static $invocations = []; + + /** + * Set a function patch + * + * This method has '__' suffix, because if it matches real function name, + * '__callStatic()' catch it. + * + * @param string $function function name + * @param mixed $return_value return value or callable + * @param string $class_name class::method to apply this patch + * + * @throws LogicException + */ + public static function patch__($function, $return_value, $class_method = null) + { + $function = strtolower($function); + + if (FunctionPatcher::isBlacklisted($function)) + { + $msg = "Can't patch on '$function'. It is in blacklist."; + self::outputMessage($msg); + throw new LogicException($msg); + } + if (! FunctionPatcher::isWhitelisted($function)) + { + $msg = "Can't patch on '$function'. It is not in whitelist. If you want to patch it, please add it to 'functions_to_patch' in 'tests/Bootstrap.php'. But note that there are some limitations. See for details."; + self::outputMessage($msg); + throw new LogicException($msg); + } + + self::$patches[$function] = $return_value; + self::$patches_to_apply[$function] = strtolower($class_method); + } + + /** + * Clear all patches and invocation data + * + * This method has '__' suffix, because if it matches real function name, + * '__callStatic()' catch it. + */ + public static function reset__() + { + self::$patches = []; + self::$patches_to_apply = []; + self::$expected_invocations = []; + self::$invocations = []; + } + + public static function setExpectedInvocations($function, $times, $params) + { + self::$expected_invocations[strtolower($function)][] = [$params, $times]; + } + + public static function verifyInvocations() + { + InvocationVerifier::verify(self::$expected_invocations, self::$invocations); + } + + protected static function logInvocation($function, $arguments) + { + if (MonkeyPatchManager::$debug) + { + $trace = debug_backtrace(); + $info = Backtrace::getInfo('FunctionPatcher', $trace); + + $file = $info['file']; + $line = $info['line']; + $method = isset($info['class_method']) ? $info['class_method'] : $info['function']; + + $log_args = function () use ($arguments) { + $output = ''; + foreach ($arguments as $arg) { + $output .= var_export($arg, true) . ', '; + } + $output = rtrim($output, ', '); + return $output; + }; + MonkeyPatchManager::log( + 'invoke_func: ' . $function . '(' . $log_args() . ') on line ' . $line . ' in ' . $file . ' by ' . $method . '()' + ); + } + } + + protected static function checkCalledMethod($function) + { + $trace = debug_backtrace(); + $info = Backtrace::getInfo('FunctionPatcher', $trace); + + $class = strtolower($info['class']); + $class_method = strtolower($info['class_method']); + + // Patches the functions only in the class + if (strpos(self::$patches_to_apply[$function], '::') === false) + { + if (self::$patches_to_apply[$function] !== $class) + { + return false; + } + return true; + } + //Patches the functions only in the class method + else + { + if (self::$patches_to_apply[$function] !== $class_method) + { + return false; + } + return true; + } + } + + public static function __callStatic($function, array $arguments) + { + $function = strtolower($function); + + self::logInvocation($function, $arguments); + self::$invocations[$function][] = $arguments; + + if (isset(self::$patches_to_apply[$function])) + { + if (! self::checkCalledMethod($function)) + { + MonkeyPatchManager::log( + 'invoke_func: ' . $function . '() not patched (out of scope)' + ); + self::checkPassedByReference($function); + return call_user_func_array($function, $arguments); + } + } + + if (array_key_exists($function, self::$patches)) + { + MonkeyPatchManager::log('invoke_func: ' . $function . '() patched'); + + if (is_callable(self::$patches[$function])) + { + $callable = self::$patches[$function]; + + $return = call_user_func_array($callable, $arguments); + if ($return !== __GO_TO_ORIG__) + { + return $return; + } + return call_user_func_array($function, $arguments); + } + + return self::$patches[$function]; + } + + MonkeyPatchManager::log( + 'invoke_func: ' . $function . '() not patched (no patch)' + ); + self::checkPassedByReference($function); + return call_user_func_array($function, $arguments); + } + + protected static function checkPassedByReference($function) + { + $ref_func = new ReflectionFunction($function); + + foreach ($ref_func->getParameters() as $param) + { + if ($param->isPassedByReference()) + { + // Add tmp blacklist + Cache::appendTmpFunctionBlacklist($function); + + // Remove cache file + $backtrace = debug_backtrace(); + $info = Backtrace::getInfo('FunctionPatcher', $backtrace); + $orig_file = $info['file']; + $cache = Cache::removeSrcCacheFile($orig_file); + + $pr_msg = ''; + if (self::isInternalFunction($function)) + { + $pr_msg = "Please send Pull Request to add function '$function' to default config.\n"; + } + + $tmp_blacklist_file = Cache::getTmpFunctionBlacklistFile(); + $msg = + "\n" + . "Can't patch on function '$function'.\n" + . "It has param(s) passed by reference.\n" + . "Added it temporary blacklist file '$tmp_blacklist_file'.\n" + . "And removed cache file '$cache'.\n" + . "$pr_msg" + . "\nPlease run phpunit again."; + + self::outputMessage($msg); + throw new LogicException($msg); + } + } + } + + protected static function outputMessage($msg) + { + $red_begin = "\033[41m\033[37m"; + $red_end = "\033[0m"; + + $msg = str_replace( + ['', ''], [$red_begin, $red_end], $msg + ); + echo $msg . "\n"; + } + + /** + * @param string $name function name + * @return bool + */ + protected static function isInternalFunction($name) + { + try { + $ref_func = new ReflectionFunction($name); + return $ref_func->isInternal(); + } catch (ReflectionException $e) { + // ReflectionException: Function xxx() does not exist + return false; + } + } + + public static function openssl_random_pseudo_bytes( + $length, &$crypto_strong = null + ) + { + $function = 'openssl_random_pseudo_bytes'; + $arguments = [$length, $crypto_strong]; + self::logInvocation($function, $arguments); + self::$invocations[$function][] = $arguments; + + if ($crypto_strong === null) + { + $crypto_strong = true; + } + + if (array_key_exists($function, self::$patches)) + { + if (is_callable(self::$patches[$function])) + { + $callable = self::$patches[$function]; + return call_user_func_array( + $callable, + [$length, &$crypto_strong] + ); + } + + return self::$patches[$function]; + } + + return openssl_random_pseudo_bytes($length, $crypto_strong); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher.php new file mode 100644 index 00000000000..ceaffdd177c --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher.php @@ -0,0 +1,80 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher; + +require __DIR__ . '/MethodPatcher/NodeVisitor.php'; +require __DIR__ . '/MethodPatcher/PatchManager.php'; + +use LogicException; + +use Kenjis\MonkeyPatch\Patcher\MethodPatcher\NodeVisitor; + +class MethodPatcher extends AbstractPatcher +{ + const CODE = <<<'EOL' +if (($__ret__ = \__PatchManager__::getReturn(__CLASS__, __FUNCTION__, func_get_args())) !== __GO_TO_ORIG__) return $__ret__; +EOL; + + public static $replacement; + + public function __construct() + { + $this->node_visitor = new NodeVisitor(); + } + + protected static function generateNewSource($source) + { + $tokens = token_get_all($source); + $new_source = ''; + $i = -1; + + ksort(self::$replacement); + reset(self::$replacement); + $replacement = each(self::$replacement); + + $start_method = false; + + foreach ($tokens as $token) + { + $i++; + + if ($i == $replacement['key']) + { + $start_method = true; + } + + if (is_string($token)) + { + if ($start_method && $token === '{') + { + $new_source .= '{ ' . self::CODE; + $start_method = false; + $replacement = each(self::$replacement); + } + else + { + $new_source .= $token; + } + } + else + { + $new_source .= $token[1]; + } + } + + if ($replacement !== false) + { + throw new LogicException('Replacement data still remain'); + } + + return $new_source; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher/NodeVisitor.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher/NodeVisitor.php new file mode 100644 index 00000000000..46998b6b6b1 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher/NodeVisitor.php @@ -0,0 +1,31 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher\MethodPatcher; + +use PhpParser\Node; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\NodeVisitorAbstract; + +use Kenjis\MonkeyPatch\Patcher\MethodPatcher; + +class NodeVisitor extends NodeVisitorAbstract +{ + public function leaveNode(Node $node) + { + if (! ($node instanceof ClassMethod)) + { + return; + } + + $pos = $node->getAttribute('startTokenPos'); + MethodPatcher::$replacement[$pos] = true; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher/PatchManager.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher/PatchManager.php new file mode 100644 index 00000000000..3b8b5afc505 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/Patcher/MethodPatcher/PatchManager.php @@ -0,0 +1,114 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch\Patcher\MethodPatcher; + +class_alias('Kenjis\MonkeyPatch\Patcher\MethodPatcher\PatchManager', '__PatchManager__'); + +use Kenjis\MonkeyPatch\MonkeyPatchManager; +use Kenjis\MonkeyPatch\Patcher\Backtrace; +use Kenjis\MonkeyPatch\InvocationVerifier; + +class PatchManager +{ + private static $patches = []; + private static $expected_invocations = []; + private static $invocations = []; + + /** + * Set a method patch + * + * @param string $class + * @param array $params [method_name => return_value] + */ + public static function set($class, $params) + { + self::$patches[$class] = $params; + } + + /** + * Clear all patches and invocation data + */ + public static function clear() + { + self::$patches = []; + self::$expected_invocations = []; + self::$invocations = []; + } + + public static function getReturn($class, $method, $params) + { + if (MonkeyPatchManager::$debug) + { + $trace = debug_backtrace(); + $info = Backtrace::getInfo('MethodPatcher', $trace); + + $file = $info['file']; + $line = $info['line']; + + if (isset($info['class_method'])) + { + $called_method = $info['class_method']; + } + elseif (isset($info['function'])) + { + $called_method = $info['function']; + } + else + { + $called_method = 'n/a'; + } + + $log_args = function () use ($params) { + $output = ''; + foreach ($params as $arg) { + $output .= var_export($arg, true) . ', '; + } + $output = rtrim($output, ', '); + return $output; + }; + MonkeyPatchManager::log( + 'invoke_method: ' . $class.'::'.$method . '(' . $log_args() . ') on line ' . $line . ' in ' . $file . ' by ' . $called_method + ); +// var_dump($trace); exit; + } + + self::$invocations[$class.'::'.$method][] = $params; + + if ( + isset(self::$patches[$class]) + && array_key_exists($method, self::$patches[$class]) + ) + { + $patch = self::$patches[$class][$method]; + } + else + { + return __GO_TO_ORIG__; + } + + if (is_callable($patch)) + { + return call_user_func_array($patch, $params); + } else { + return $patch; + } + } + + public static function setExpectedInvocations($class_method, $times, $params) + { + self::$expected_invocations[$class_method][] = [$params, $times]; + } + + public static function verifyInvocations() + { + InvocationVerifier::verify(self::$expected_invocations, self::$invocations); + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/PathChecker.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/PathChecker.php new file mode 100644 index 00000000000..c1a7f399b3e --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/PathChecker.php @@ -0,0 +1,113 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +namespace Kenjis\MonkeyPatch; + +use RuntimeException; + +class PathChecker +{ + private static $include_paths = []; + private static $exclude_paths = []; + + /** + * @param array $paths directory or file path + * @return array + * @throws RuntimeException + */ + protected static function normalizePaths(array $paths) + { + $new_paths = []; + $excluded = false; + foreach ($paths as $path) + { + // Path starting with '-' has special meaning (excluding it) + if (substr($path, 0, 1) === '-') + { + $excluded = true; + $path = ltrim($path, '-'); + } + + $real_path = realpath($path); + if ($real_path === FALSE) + { + throw new RuntimeException($path . ' does not exist?'); + } + if (is_dir($real_path)) + { + $real_path = $real_path . '/'; + } + $new_paths[] = $excluded ? '-'.$real_path : $real_path; + } + array_unique($new_paths, SORT_STRING); + sort($new_paths, SORT_STRING); + return $new_paths; + } + + public static function setIncludePaths(array $dir) + { + self::$include_paths = self::normalizePaths($dir); + } + + public static function setExcludePaths(array $dir) + { + self::$exclude_paths = self::normalizePaths($dir); + } + + public static function getIncludePaths() + { + return self::$include_paths; + } + + public static function getExcludePaths() + { + return self::$exclude_paths; + } + + public static function check($path) + { + // Whitelist first + $is_white = false; + foreach (self::$include_paths as $white_dir) { + $len = strlen($white_dir); + if (substr($path, 0, $len) === $white_dir) + { + $is_white = true; + } + } + if ($is_white === false) + { + return false; + } + + // Then blacklist + foreach (self::$exclude_paths as $black_dir) { + // Check excluded path that starts with '-'. + // '-' is smaller than '/', so this checking always comes first. + if (substr($black_dir, 0, 1) === '-') + { + $black_dir = ltrim($black_dir, '-'); + $len = strlen($black_dir); + if (substr($path, 0, $len) === $black_dir) + { + return true; + } + } + + $len = strlen($black_dir); + if (substr($path, 0, $len) === $black_dir) + { + return false; + } + } + + return true; + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/bootstrap.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/bootstrap.php new file mode 100644 index 00000000000..5595e06d881 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/bootstrap.php @@ -0,0 +1,57 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +// Autoloader for PHP-Parser +// Don't use `require`, because we must require it in CIPHPUnitTest::init() +// for providing autoloading when we don't use Monkey Patching +require_once __DIR__ . '/third_party/PHP-Parser/lib/bootstrap.php'; + +require __DIR__ . '/IncludeStream.php'; +require __DIR__ . '/PathChecker.php'; +require __DIR__ . '/MonkeyPatchManager.php'; +require __DIR__ . '/MonkeyPatch.php'; +require __DIR__ . '/Cache.php'; +require __DIR__ . '/InvocationVerifier.php'; + +require __DIR__ . '/functions/exit__.php'; + +const __GO_TO_ORIG__ = '__GO_TO_ORIG__'; + +class_alias('Kenjis\MonkeyPatch\MonkeyPatchManager', 'MonkeyPatchManager'); + +// And you have to configure for your application +//MonkeyPatchManager::init([ +// // PHP Parser: PREFER_PHP7, PREFER_PHP5, ONLY_PHP7, ONLY_PHP5 +// 'php_parser' => 'PREFER_PHP5', +// // Project root directory +// 'root_dir' => APPPATH . '../', +// // Cache directory +// 'cache_dir' => APPPATH . 'tests/_ci_phpunit_test/tmp/cache', +// // Directories to patch on source files +// 'include_paths' => [ +// APPPATH, +// BASEPATH, +// ], +// // Excluding directories to patch +// 'exclude_paths' => [ +// APPPATH . 'tests/', +// ], +// // All patchers you use +// 'patcher_list' => [ +// 'ExitPatcher', +// 'FunctionPatcher', +// 'MethodPatcher', +// 'ConstantPatcher', +// ], +// // Additional functions to patch +// 'functions_to_patch' => [ +// //'random_string', +// ], +//]); diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/functions/exit__.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/functions/exit__.php new file mode 100644 index 00000000000..c0740e892ff --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/functions/exit__.php @@ -0,0 +1,37 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +function exit__($status = null) +{ + $trace = debug_backtrace(); + $file = $trace[0]['file']; + $line = $trace[0]['line']; + $class = isset($trace[1]['class']) ? $trace[1]['class'] : null; + $method = $trace[1]['function']; + + if ($class === null) + { + $message = 'exit() called in ' . $method . '() function'; + } + else + { + $message = 'exit() called in ' . $class . '::' . $method . '()'; + } + + $exception_name = Kenjis\MonkeyPatch\MonkeyPatchManager::getExitExceptionClassname(); + $exception = new $exception_name($message); + $exception->file = $file; + $exception->line = $line; + $exception->class = $class; + $exception->method = $method; + $exception->exit_status = $status; + + throw $exception; +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/.gitignore b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/.gitignore new file mode 100644 index 00000000000..8c7db2a6efc --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +grammar/kmyacc.exe +grammar/y.output diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/.travis.yml b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/.travis.yml new file mode 100644 index 00000000000..d4a55a50bca --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/.travis.yml @@ -0,0 +1,26 @@ +language: php + +sudo: false + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - nightly + - hhvm + +install: + - if [ $TRAVIS_PHP_VERSION = '5.6' ]; then composer require satooshi/php-coveralls '~1.0'; fi + - composer install --prefer-source + +matrix: + allow_failures: + - php: nightly + fast_finish: true + +script: + if [ $TRAVIS_PHP_VERSION = '5.6' ]; then vendor/bin/phpunit --coverage-clover build/logs/clover.xml; else vendor/bin/phpunit; fi + +after_success: + if [ $TRAVIS_PHP_VERSION = '5.6' ]; then php vendor/bin/coveralls; fi diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/CHANGELOG.md b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/CHANGELOG.md new file mode 100644 index 00000000000..d282076c666 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/CHANGELOG.md @@ -0,0 +1,329 @@ +Version 2.0.2-dev +----------------- + +Nothing yet. + +Version 2.0.1 (2016-02-28) +-------------------------- + +### Fixed + +* `declare() {}` and `declare();` are not semantically equivalent and will now result in different + ASTs. The format case will have an empty `stmts` array, while the latter will set `stmts` to + `null`. +* Magic constants are now supported as semi-reserved keywords. +* A shebang line like `#!/usr/bin/env php` is now allowed at the start of a namespaced file. + Previously this generated an exception. +* The `prettyPrintFile()` method will not strip a trailing `?>` from the raw data that follows a + `__halt_compiler()` statement. +* The `prettyPrintFile()` method will not strip an opening `slice()` which takes a subslice of a name. + +### Changed + +* `PhpParser\Parser` is now an interface, implemented by `Parser\Php5`, `Parser\Php7` and + `Parser\Multiple`. The `Multiple` parser will try multiple parsers, until one succeeds. +* Token constants are now defined on `PhpParser\Parser\Tokens` rather than `PhpParser\Parser`. +* The `Name->set()`, `Name->append()`, `Name->prepend()` and `Name->setFirst()` methods are + deprecated in favor of `Name::concat()` and `Name->slice()`. +* The `NodeTraverser` no longer clones nodes by default. The old behavior can be restored by + passing `true` to the constructor. +* The constructor for `Scalar` nodes no longer has a default value. E.g. `new LNumber()` should now + be written as `new LNumber(0)`. + +Version 1.4.0 (2015-07-14) +-------------------------- + +### Added + +* Added interface `PhpParser\Node\FunctionLike`, which is implemented by `Stmt\ClassMethod`, + `Stmt\Function_` and `Expr\Closure` nodes. This interface provides getters for their common + subnodes. +* Added `Node\Stmt\ClassLike::getMethod()` to look up a specific method on a class/interface/trait. + +### Fixed + +* Fixed `isPublic()` return value for implicitly public properties and methods that define and + additional modifier like `static` or `abstract`. +* Properties are now accepted by the trait builder. +* Fixed `__HALT_COMPILER_OFFSET__` support on HHVM. + +Version 1.3.0 (2015-05-02) +-------------------------- + +### Added + +* Errors can now store the attributes of the node/token where the error occurred. Previously only the start line was + stored. +* If file positions are enabled in the lexer, errors can now provide column information if it is available. See + [documentation](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Error.markdown#column-information). +* The parser now provides an experimental error recovery mode, which can be enabled by disabling the `throwOnError` + parser option. In this mode the parser will try to construct a partial AST even if the code is not valid PHP. See + [documentation](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Error.markdown#error-recovery). +* Added support for PHP 7 `yield from` expression. It is represented by `Expr\YieldFrom`. +* Added support for PHP 7 anonymous classes. These are represented by ordinary `Stmt\Class_` nodes with the name set to + `null`. Furthermore this implies that `Expr\New_` can now contain a `Stmt\Class_` in its `class` subnode. + +### Fixed + +* Fixed registration of PHP 7 aliases, for the case where the old name was used before the new name. +* Fixed handling of precedence when pretty-printing `print` expressions. +* Floating point numbers are now pretty-printed with a higher precision. +* Checks for special class names like `self` are now case-insensitive. + +Version 1.2.2 (2015-04-03) +-------------------------- + +* The `NameResolver` now resolves parameter type hints when entering the function/method/closure node. As such other + visitors running after it will be able to make use of the resolved names at that point already. +* The autoloader no longer sets the `unserialize_callback_func` ini option on registration - this is not necessary and + may cause issues when running PhpUnit tests with process isolation. + +Version 1.2.1 (2015-03-24) +-------------------------- + +* Fixed registration of the aliases introduced in 1.2.0. Previously the old class names could not be used in + `instanceof` checks under some circumstances. + +Version 1.2.0 (2015-03-22) +-------------------------- + +### Changed + +* To ensure compatibility with PHP 7, the following node classes have been renamed: + + OLD => NEW + PhpParser\Node\Expr\Cast\Bool => PhpParser\Node\Expr\Cast\Bool_ + PhpParser\Node\Expr\Cast\Int => PhpParser\Node\Expr\Cast\Int_ + PhpParser\Node\Expr\Cast\Object => PhpParser\Node\Expr\Cast\Object_ + PhpParser\Node\Expr\Cast\String => PhpParser\Node\Expr\Cast\String_ + PhpParser\Node\Scalar\String => PhpParser\Node\Scalar\String_ + + **The previous class names are still supported as aliases.** However it is strongly encouraged to use the new names + in order to make your code compatible with PHP 7. + +* Subnodes are now stored using real properties instead of an array. This improves performance and memory usage of the + initial parse and subsequent node tree operations. The `NodeAbstract` class still supports the old way of specifying + subnodes, however this is *deprecated*. In any case properties that are assigned to a node after creation will no + longer be considered as subnodes. + +* Methods and property declarations will no longer set the `Stmt\Class_::MODIFIER_PUBLIC` flag if no visibility is + explicitly given. However the `isPublic()` method will continue to return true. This allows you to distinguish whether + a method/property is explicitly or implicitly public and control the pretty printer output more precisely. + +* The `Stmt\Class_`, `Stmt\Interface_` and `Stmt\Trait_` nodes now inherit from `Stmt\ClassLike`, which provides a + `getMethods()` method. Previously this method was only available on `Stmt\Class_`. + +* Support including the `bootstrap.php` file multiple times. + +* Make documentation and tests part of the release tarball again. + +* Improve support for HHVM and PHP 7. + +### Added + +* Added support for PHP 7 return type declarations. This adds an additional `returnType` subnode to `Stmt\Function_`, + `Stmt\ClassMethod` and `Expr\Closure`. + +* Added support for the PHP 7 null coalesce operator `??`. The operator is represented by `Expr\BinaryOp\Coalesce`. + +* Added support for the PHP 7 spaceship operator `<=>`. The operator is represented by `Expr\BinaryOp\Spaceship`. + +* Added use builder. + +* Added global namespace support to the namespace builder. + +* Added a constructor flag to `NodeTraverser`, which disables cloning of nodes. + +Version 1.1.0 (2015-01-18) +-------------------------- + +* Methods that do not specify an explicit visibility (e.g. `function method()`) will now have the `MODIFIER_PUBLIC` + flag set. This also means that their `isPublic()` method will return true. + +* Declaring a property as abstract or final is now an error. + +* The `Lexer` and `Lexer\Emulative` classes now accept an `$options` array in their constructors. Currently only the + `usedAttributes` option is supported, which determines which attributes will be added to AST nodes. In particular + it is now possible to add information on the token and file positions corresponding to a node. For more details see + the [Lexer component](https://github.com/nikic/PHP-Parser/blob/master/doc/component/Lexer.markdown) documentation. + +* Node visitors can now return `NodeTraverser::DONT_TRAVERSE_CHILDREN` from `enterNode()` in order to skip all children + of the current node, for all visitors. + +* Added builders for traits and namespaces. + +* The class, interface, trait, function, method and property builders now support adding doc comments using the + `setDocComment()` method. + +* Added support for fully-qualified and namespace-relative names in builders. No longer allow use of name component + arrays. + +* Do not add documentation and tests to distribution archive files. + +Version 1.0.2 (2014-11-04) +-------------------------- + +* The `NameResolver` visitor now also resolves names in trait adaptations (aliases and precedence declarations). + +* Remove stray whitespace when pretty-printing trait adaptations that only change visibility. + +Version 1.0.1 (2014-10-14) +-------------------------- + +* Disallow `new` expressions without a class name. Previously `new;` was accidentally considered to be valid code. + +* Support T_ONUMBER token used by HHVM. + +* Add ability to directly pass code to the `php-parse.php` script. + +* Prevent truncation of `var_dump()` output in the `php-parse.php` script if XDebug is used. + +Version 1.0.0 (2014-09-12) +-------------------------- + +* [BC] Removed deprecated `Template` and `TemplateLoader` classes. + +* Fixed XML unserializer to properly work with new namespaced node names. + +Version 1.0.0-beta2 (2014-08-31) +-------------------------------- + +* [PHP 5.6] Updated support for constant scalar expressions to comply with latest changes. This means that arrays + and array dimension fetches are now supported as well. + +* [PHP 5.6] Direct array dereferencing of constants is supported now, i.e. both `FOO[0]` and `Foo::BAR[0]` are valid + now. + +* Fixed handling of special class names (`self`, `parent` and `static`) in the name resolver to be case insensitive. + Additionally the name resolver now enforces that special class names are only used as unqualified names, e.g. `\self` + is considered invalid. + +* The case of references to the `static` class name is now preserved. Previously `static` was always lowercased, + regardless of the case used in the source code. + +* The autoloader now only requires a file if it exists. This allows usages like + `class_exists('PhpParser\NotExistingClass')`. + +* Added experimental `bin/php-parse.php` script, which is intended to help exploring and debugging the node tree. + +* Separated the parser implemention (in `lib/PhpParser/ParserAbstract.php`) and the generated data (in + `lib/PhpParser/Parser.php`). Furthermore the parser now uses meaningful variable names and contains comments + explaining their usage. + +Version 1.0.0-beta1 (2014-03-27) +-------------------------------- + +* [BC] PHP-Parser now requires PHP 5.3 or newer to run. It is however still possible to *parse* PHP 5.2 source code, + while running on a newer version. + +* [BC] The library has been moved to use namespaces with the `PhpParser` vendor prefix. However, the old names using + underscores are still available as aliases, as such most code should continue running on the new version without + further changes. + + However, code performing dispatch operations on `Node::getType()` may be affected by some of the name changes. For + example a `+` node will now return type `Expr_BinaryOp_Plus` instead of `Expr_Plus`. In particular this may affect + custom pretty printers. + + Due to conflicts with reserved keywords, some class names now end with an underscore, e.g. `PHPParser_Node_Stmt_Class` + is now `PhpParser\Node\Stmt\Class_`. (But as usual, the old name is still available) + +* [PHP 5.6] Added support for the power operator `**` (node `Expr\BinaryOp\Pow`) and the compound power assignment + operator `**=` (node `Expr\AssignOp\Pow`). + +* [PHP 5.6] Added support for variadic functions: `Param` nodes now have `variadic` as a boolean subnode. + +* [PHP 5.6] Added support for argument unpacking: `Arg` nodes now have `unpack` as a boolean subnode. + +* [PHP 5.6] Added support for aliasing of functions and constants. `Stmt\Use_` nodes now have an integral `type` + subnode, which is one of `Stmt\Use_::TYPE_NORMAL` (`use`), `Stmt\Use_::TYPE_FUNCTION` (`use function`) or + `Stmt\Use_::TYPE_CONSTANT` (`use const`). + + The `NameResolver` now also supports resolution of such aliases. + +* [PHP 5.6] Added support for constant scalar expressions. This means that certain expressions are now allowed as the + initializer for constants, properties, parameters, static variables, etc. + +* [BC] Improved pretty printing of empty statements lists, which are now printed as `{\n}` instead of `{\n \n}`. + This changes the behavior of the protected `PrettyPrinterAbstract::pStmts()` method, so custom pretty printing code + making use it of may need to be adjusted. + +* Changed the order of some subnodes to be consistent with their order in the sour code. For example `Stmt\If->cond` + will now appear before `Stmt\If->stmts` etc. + +* Added `Scalar\MagicConstant->getName()`, which returns the name of the magic constant (e.g. `__CLASS__`). + +**The following changes are also included in 0.9.5**: + +* [BC] Deprecated `PHPParser_Template` and `PHPParser_TemplateLoader`. This functionality does not belong in the main project + and - as far as I know - nobody is using it. + +* Add `NodeTraverser::removeVisitor()` method, which removes a visitor from the node traverser. This also modifies the + corresponding `NodeTraverserInterface`. + +* Fix alias resolution in `NameResolver`: Class names are now correctly handled as case-insensitive. + +* The undefined variable error, which is used to the lexer to reset the error state, will no longer interfere with + custom error handlers. + +--- + +**This changelog only includes changes from the 1.0 and 2.0 series. For older changes see the +[0.9 series changelog][https://github.com/nikic/PHP-Parser/blob/0.9/CHANGELOG.md].** \ No newline at end of file diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/LICENSE b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/LICENSE new file mode 100644 index 00000000000..443210b44ab --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2011 by Nikita Popov. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/README.md b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/README.md new file mode 100644 index 00000000000..e8b5e0b88ae --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/README.md @@ -0,0 +1,96 @@ +PHP Parser +========== + +[![Build Status](https://travis-ci.org/nikic/PHP-Parser.svg?branch=master)](https://travis-ci.org/nikic/PHP-Parser) [![Coverage Status](https://coveralls.io/repos/github/nikic/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/nikic/PHP-Parser?branch=master) + +This is a PHP 5.2 to PHP 7.0 parser written in PHP. Its purpose is to simplify static code analysis and +manipulation. + +[**Documentation for version 2.x**][doc_master] (stable; for running on PHP >= 5.4; for parsing PHP 5.2 to PHP 7.0). + +[Documentation for version 1.x][doc_1_x] (stable; for running on PHP >= 5.3; for parsing PHP 5.2 to PHP 5.6). + +In a Nutshell +------------- + +The parser turns PHP source code into an abstract syntax tree. For example, if you pass the following code into the +parser: + +```php + Expr_AssignOp_BitwiseAnd +Expr_AssignBitwiseOr => Expr_AssignOp_BitwiseOr +Expr_AssignBitwiseXor => Expr_AssignOp_BitwiseXor +Expr_AssignConcat => Expr_AssignOp_Concat +Expr_AssignDiv => Expr_AssignOp_Div +Expr_AssignMinus => Expr_AssignOp_Minus +Expr_AssignMod => Expr_AssignOp_Mod +Expr_AssignMul => Expr_AssignOp_Mul +Expr_AssignPlus => Expr_AssignOp_Plus +Expr_AssignShiftLeft => Expr_AssignOp_ShiftLeft +Expr_AssignShiftRight => Expr_AssignOp_ShiftRight + +Expr_BitwiseAnd => Expr_BinaryOp_BitwiseAnd +Expr_BitwiseOr => Expr_BinaryOp_BitwiseOr +Expr_BitwiseXor => Expr_BinaryOp_BitwiseXor +Expr_BooleanAnd => Expr_BinaryOp_BooleanAnd +Expr_BooleanOr => Expr_BinaryOp_BooleanOr +Expr_Concat => Expr_BinaryOp_Concat +Expr_Div => Expr_BinaryOp_Div +Expr_Equal => Expr_BinaryOp_Equal +Expr_Greater => Expr_BinaryOp_Greater +Expr_GreaterOrEqual => Expr_BinaryOp_GreaterOrEqual +Expr_Identical => Expr_BinaryOp_Identical +Expr_LogicalAnd => Expr_BinaryOp_LogicalAnd +Expr_LogicalOr => Expr_BinaryOp_LogicalOr +Expr_LogicalXor => Expr_BinaryOp_LogicalXor +Expr_Minus => Expr_BinaryOp_Minus +Expr_Mod => Expr_BinaryOp_Mod +Expr_Mul => Expr_BinaryOp_Mul +Expr_NotEqual => Expr_BinaryOp_NotEqual +Expr_NotIdentical => Expr_BinaryOp_NotIdentical +Expr_Plus => Expr_BinaryOp_Plus +Expr_ShiftLeft => Expr_BinaryOp_ShiftLeft +Expr_ShiftRight => Expr_BinaryOp_ShiftRight +Expr_Smaller => Expr_BinaryOp_Smaller +Expr_SmallerOrEqual => Expr_BinaryOp_SmallerOrEqual + +Scalar_ClassConst => Scalar_MagicConst_Class +Scalar_DirConst => Scalar_MagicConst_Dir +Scalar_FileConst => Scalar_MagicConst_File +Scalar_FuncConst => Scalar_MagicConst_Function +Scalar_LineConst => Scalar_MagicConst_Line +Scalar_MethodConst => Scalar_MagicConst_Method +Scalar_NSConst => Scalar_MagicConst_Namespace +Scalar_TraitConst => Scalar_MagicConst_Trait +``` + +These changes may affect custom pretty printers and code comparing the return value of `Node::getType()` to specific +strings. + +### Miscellaneous + + * The classes `Template` and `TemplateLoader` have been removed. You should use some other [code generation][code_gen] + project built on top of PHP-Parser instead. + + * The `PrettyPrinterAbstract::pStmts()` method now emits a leading newline if the statement list is not empty. + Custom pretty printers should remove the explicit newline before `pStmts()` calls. + + Old: + + ```php + public function pStmt_Trait(PHPParser_Node_Stmt_Trait $node) { + return 'trait ' . $node->name + . "\n" . '{' . "\n" . $this->pStmts($node->stmts) . "\n" . '}'; + } + ``` + + New: + + ```php + public function pStmt_Trait(Stmt\Trait_ $node) { + return 'trait ' . $node->name + . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}'; + } + ``` + + [code_gen]: https://github.com/nikic/PHP-Parser/wiki/Projects-using-the-PHP-Parser#code-generation \ No newline at end of file diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/UPGRADE-2.0.md b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/UPGRADE-2.0.md new file mode 100644 index 00000000000..1c61de54fb5 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/UPGRADE-2.0.md @@ -0,0 +1,74 @@ +Upgrading from PHP-Parser 1.x to 2.0 +==================================== + +### PHP version requirements + +PHP-Parser now requires PHP 5.4 or newer to run. It is however still possible to *parse* PHP 5.2 and +PHP 5.3 source code, while running on a newer version. + +### Creating a parser instance + +Parser instances should now be created through the `ParserFactory`. Old direct instantiation code +will not work, because the parser class was renamed. + +Old: + +```php +use PhpParser\Parser, PhpParser\Lexer; +$parser = new Parser(new Lexer\Emulative); +``` + +New: + +```php +use PhpParser\ParserFactory; +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); +``` + +The first argument to `ParserFactory` determines how different PHP versions are handled. The +possible values are: + + * `ParserFactory::PREFER_PHP7`: Try to parse code as PHP 7. If this fails, try to parse it as PHP 5. + * `ParserFactory::PREFER_PHP5`: Try to parse code as PHP 5. If this fails, try to parse it as PHP 7. + * `ParserFactory::ONLY_PHP7`: Parse code as PHP 7. + * `ParserFactory::ONLY_PHP5`: Parse code as PHP 5. + +For most practical purposes the difference between `PREFER_PHP7` and `PREFER_PHP5` is mainly whether +a scalar type hint like `string` will be stored as `'string'` (PHP 7) or as `new Name('string')` +(PHP 5). + +To use a custom lexer, pass it as the second argument to the `create()` method: + +```php +use PhpParser\ParserFactory; +$lexer = new MyLexer; +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7, $lexer); +``` + +### Rename of the `PhpParser\Parser` class + +`PhpParser\Parser` is now an interface, which is implemented by `Parser\Php5`, `Parser\Php7` and +`Parser\Multiple`. Parser tokens are now defined in `Parser\Tokens`. If you use the `ParserFactory` +described above to create your parser instance, these changes should have no further impact on you. + +### Removal of legacy aliases + +All legacy aliases for classes have been removed. This includes the old non-namespaced `PHPParser_` +classes, as well as the classes that had to be renamed for PHP 7 support. + +### Deprecations + +The `set()`, `setFirst()`, `append()` and `prepend()` methods of the `Node\Name` class have been +deprecated. Instead `Name::concat()` and `Name->slice()` should be used. + +### Miscellaneous + +* The `NodeTraverser` no longer clones nodes by default. If you want to restore the old behavior, + pass `true` to the constructor. +* The legacy node format has been removed. If you use custom nodes, they are now expected to + implement a `getSubNodeNames()` method. +* The default value for `Scalar` node constructors was removed. This means that something like + `new LNumber()` should be replaced by `new LNumber(0)`. +* String parts of encapsed strings are now represented using `Scalar\EncapsStringPart` nodes, while + previously raw strings were used. This affects the `parts` child of `Scalar\Encaps` and + `Expr\ShellExec`. \ No newline at end of file diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/bin/php-parse b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/bin/php-parse new file mode 100644 index 00000000000..c3a46cfdaa8 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/bin/php-parse @@ -0,0 +1,166 @@ +#!/usr/bin/env php + array( + 'startLine', 'endLine', 'startFilePos', 'endFilePos' +))); +$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer); +$dumper = new PhpParser\NodeDumper; +$prettyPrinter = new PhpParser\PrettyPrinter\Standard; +$serializer = new PhpParser\Serializer\XML; + +$traverser = new PhpParser\NodeTraverser(); +$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); + +foreach ($files as $file) { + if (strpos($file, ' Code $code\n"; + } else { + if (!file_exists($file)) { + die("File $file does not exist.\n"); + } + + $code = file_get_contents($file); + echo "====> File $file:\n"; + } + + try { + $stmts = $parser->parse($code); + } catch (PhpParser\Error $e) { + if ($attributes['with-column-info'] && $e->hasColumnInfo()) { + $startLine = $e->getStartLine(); + $endLine = $e->getEndLine(); + $startColumn = $e->getStartColumn($code); + $endColumn = $e->getEndColumn($code); + $message .= $e->getRawMessage() . " from $startLine:$startColumn to $endLine:$endColumn"; + } else { + $message = $e->getMessage(); + } + + die($message . "\n"); + } + + foreach ($operations as $operation) { + if ('dump' === $operation) { + echo "==> Node dump:\n"; + echo $dumper->dump($stmts), "\n"; + } elseif ('pretty-print' === $operation) { + echo "==> Pretty print:\n"; + echo $prettyPrinter->prettyPrintFile($stmts), "\n"; + } elseif ('serialize-xml' === $operation) { + echo "==> Serialized XML:\n"; + echo $serializer->serialize($stmts), "\n"; + } elseif ('var-dump' === $operation) { + echo "==> var_dump():\n"; + var_dump($stmts); + } elseif ('resolve-names' === $operation) { + echo "==> Resolved names.\n"; + $stmts = $traverser->traverse($stmts); + } + } +} + +function showHelp($error) { + die($error . "\n\n" . + << false, + ); + + array_shift($args); + $parseOptions = true; + foreach ($args as $arg) { + if (!$parseOptions) { + $files[] = $arg; + continue; + } + + switch ($arg) { + case '--dump': + case '-d': + $operations[] = 'dump'; + break; + case '--pretty-print': + case '-p': + $operations[] = 'pretty-print'; + break; + case '--serialize-xml': + $operations[] = 'serialize-xml'; + break; + case '--var-dump': + $operations[] = 'var-dump'; + break; + case '--resolve-names': + case '-N'; + $operations[] = 'resolve-names'; + break; + case '--with-column-info': + case '-c'; + $attributes['with-column-info'] = true; + break; + case '--': + $parseOptions = false; + break; + default: + if ($arg[0] === '-') { + showHelp("Invalid operation $arg."); + } else { + $files[] = $arg; + } + } + } + + return array($operations, $files, $attributes); +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/composer.json b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/composer.json new file mode 100644 index 00000000000..e494f4329bd --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/composer.json @@ -0,0 +1,30 @@ +{ + "name": "nikic/php-parser", + "description": "A PHP parser written in PHP", + "keywords": ["php", "parser"], + "type": "library", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Nikita Popov" + } + ], + "require": { + "php": ">=5.4", + "ext-tokenizer": "*" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "bin": ["bin/php-parse"], + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + } +} diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/0_Introduction.markdown b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/0_Introduction.markdown new file mode 100644 index 00000000000..fc2c03891c3 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/0_Introduction.markdown @@ -0,0 +1,80 @@ +Introduction +============ + +This project is a PHP 5.2 to PHP 7.0 parser **written in PHP itself**. + +What is this for? +----------------- + +A parser is useful for [static analysis][0], manipulation of code and basically any other +application dealing with code programmatically. A parser constructs an [Abstract Syntax Tree][1] +(AST) of the code and thus allows dealing with it in an abstract and robust way. + +There are other ways of processing source code. One that PHP supports natively is using the +token stream generated by [`token_get_all`][2]. The token stream is much more low level than +the AST and thus has different applications: It allows to also analyze the exact formatting of +a file. On the other hand the token stream is much harder to deal with for more complex analysis. +For example an AST abstracts away the fact that in PHP variables can be written as `$foo`, but also +as `$$bar`, `${'foobar'}` or even `${!${''}=barfoo()}`. You don't have to worry about recognizing +all the different syntaxes from a stream of tokens. + +Another question is: Why would I want to have a PHP parser *written in PHP*? Well, PHP might not be +a language especially suited for fast parsing, but processing the AST is much easier in PHP than it +would be in other, faster languages like C. Furthermore the people most probably wanting to do +programmatic PHP code analysis are incidentally PHP developers, not C developers. + +What can it parse? +------------------ + +The parser supports parsing PHP 5.2-5.6 and PHP 7. + +As the parser is based on the tokens returned by `token_get_all` (which is only able to lex the PHP +version it runs on), additionally a wrapper for emulating new tokens from 5.5, 5.6 and 7.0 is +provided. This allows to parse PHP 7.0 source code running on PHP 5.4, for example. This emulation +is somewhat hacky and not perfect, but it should work well on any sane code. + +What output does it produce? +---------------------------- + +The parser produces an [Abstract Syntax Tree][1] (AST) also known as a node tree. How this looks like +can best be seen in an example. The program `create(ParserFactory::PREFER_PHP7); +``` + +The factory accepts a kind argument, that determines how different PHP versions are treated: + +Kind | Behavior +-----|--------- +`ParserFactory::PREFER_PHP7` | Try to parse code as PHP 7. If this fails, try to parse it as PHP 5. +`ParserFactory::PREFER_PHP5` | Try to parse code as PHP 5. If this fails, try to parse it as PHP 7. +`ParserFactory::ONLY_PHP7` | Parse code as PHP 7. +`ParserFactory::ONLY_PHP5` | Parse code as PHP 5. + +Unless you have strong reason to use something else, `PREFER_PHP7` is a reasonable default. + +The `create()` method optionally accepts a `Lexer` instance as the second argument. Some use cases +that require customized lexers are discussed in the [lexer documentation](component/Lexer.markdown). + +Subsequently you can pass PHP code (including the opening `create(ParserFactory::PREFER_PHP7); + +try { + $stmts = $parser->parse($code); + // $stmts is an array of statement nodes +} catch (Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +A parser instance can be reused to parse multiple files. + +Node tree +--------- + +If you use the above code with `$code = "subNodeName`. The `Stmt\Echo_` node has only one subnode `exprs`. So in order to access it +in the above example you would write `$stmts[0]->exprs`. If you wanted to access the name of the function +call, you would write `$stmts[0]->exprs[1]->name`. + +All nodes also define a `getType()` method that returns the node type. The type is the class name +without the `PhpParser\Node\` prefix and `\` replaced with `_`. It also does not contain a trailing +`_` for reserved-keyword class names. + +It is possible to associate custom metadata with a node using the `setAttribute()` method. This data +can then be retrieved using `hasAttribute()`, `getAttribute()` and `getAttributes()`. + +By default the lexer adds the `startLine`, `endLine` and `comments` attributes. `comments` is an array +of `PhpParser\Comment[\Doc]` instances. + +The start line can also be accessed using `getLine()`/`setLine()` (instead of `getAttribute('startLine')`). +The last doc comment from the `comments` attribute can be obtained using `getDocComment()`. + +Pretty printer +-------------- + +The pretty printer component compiles the AST back to PHP code. As the parser does not retain formatting +information the formatting is done using a specified scheme. Currently there is only one scheme available, +namely `PhpParser\PrettyPrinter\Standard`. + +```php +use PhpParser\Error; +use PhpParser\ParserFactory; +use PhpParser\PrettyPrinter; + +$code = "create(ParserFactory::PREFER_PHP7); +$prettyPrinter = new PrettyPrinter\Standard; + +try { + // parse + $stmts = $parser->parse($code); + + // change + $stmts[0] // the echo statement + ->exprs // sub expressions + [0] // the first of them (the string node) + ->value // it's value, i.e. 'Hi ' + = 'Hello '; // change to 'Hello ' + + // pretty print + $code = $prettyPrinter->prettyPrint($stmts); + + echo $code; +} catch (Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +The above code will output: + + parse()`, then changed and then +again converted to code using `PhpParser\PrettyPrinter\Standard->prettyPrint()`. + +The `prettyPrint()` method pretty prints a statements array. It is also possible to pretty print only a +single expression using `prettyPrintExpr()`. + +The `prettyPrintFile()` method can be used to print an entire file. This will include the opening `create(ParserFactory::PREFER_PHP7); +$traverser = new NodeTraverser; +$prettyPrinter = new PrettyPrinter\Standard; + +// add your visitor +$traverser->addVisitor(new MyNodeVisitor); + +try { + $code = file_get_contents($fileName); + + // parse + $stmts = $parser->parse($code); + + // traverse + $stmts = $traverser->traverse($stmts); + + // pretty print + $code = $prettyPrinter->prettyPrintFile($stmts); + + echo $code; +} catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +The corresponding node visitor might look like this: + +```php +use PhpParser\Node; +use PhpParser\NodeVisitorAbstract; + +class MyNodeVisitor extends NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Scalar\String_) { + $node->value = 'foo'; + } + } +} +``` + +The above node visitor would change all string literals in the program to `'foo'`. + +All visitors must implement the `PhpParser\NodeVisitor` interface, which defines the following four +methods: + +```php +public function beforeTraverse(array $nodes); +public function enterNode(\PhpParser\Node $node); +public function leaveNode(\PhpParser\Node $node); +public function afterTraverse(array $nodes); +``` + +The `beforeTraverse()` method is called once before the traversal begins and is passed the nodes the +traverser was called with. This method can be used for resetting values before traversation or +preparing the tree for traversal. + +The `afterTraverse()` method is similar to the `beforeTraverse()` method, with the only difference that +it is called once after the traversal. + +The `enterNode()` and `leaveNode()` methods are called on every node, the former when it is entered, +i.e. before its subnodes are traversed, the latter when it is left. + +All four methods can either return the changed node or not return at all (i.e. `null`) in which +case the current node is not changed. + +The `enterNode()` method can additionally return the value `NodeTraverser::DONT_TRAVERSE_CHILDREN`, +which instructs the traverser to skip all children of the current node. + +The `leaveNode()` method can additionally return the value `NodeTraverser::REMOVE_NODE`, in which +case the current node will be removed from the parent array. Furthermore it is possible to return +an array of nodes, which will be merged into the parent array at the offset of the current node. +I.e. if in `array(A, B, C)` the node `B` should be replaced with `array(X, Y, Z)` the result will +be `array(A, X, Y, Z, C)`. + +Instead of manually implementing the `NodeVisitor` interface you can also extend the `NodeVisitorAbstract` +class, which will define empty default implementations for all the above methods. + +The NameResolver node visitor +----------------------------- + +One visitor is already bundled with the package: `PhpParser\NodeVisitor\NameResolver`. This visitor +helps you work with namespaced code by trying to resolve most names to fully qualified ones. + +For example, consider the following code: + + use A as B; + new B\C(); + +In order to know that `B\C` really is `A\C` you would need to track aliases and namespaces yourself. +The `NameResolver` takes care of that and resolves names as far as possible. + +After running it most names will be fully qualified. The only names that will stay unqualified are +unqualified function and constant names. These are resolved at runtime and thus the visitor can't +know which function they are referring to. In most cases this is a non-issue as the global functions +are meant. + +Also the `NameResolver` adds a `namespacedName` subnode to class, function and constant declarations +that contains the namespaced name instead of only the shortname that is available via `name`. + +Example: Converting namespaced code to pseudo namespaces +-------------------------------------------------------- + +A small example to understand the concept: We want to convert namespaced code to pseudo namespaces +so it works on 5.2, i.e. names like `A\\B` should be converted to `A_B`. Note that such conversions +are fairly complicated if you take PHP's dynamic features into account, so our conversion will +assume that no dynamic features are used. + +We start off with the following base code: + +```php +use PhpParser\ParserFactory; +use PhpParser\PrettyPrinter; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; + +$inDir = '/some/path'; +$outDir = '/some/other/path'; + +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); +$traverser = new NodeTraverser; +$prettyPrinter = new PrettyPrinter\Standard; + +$traverser->addVisitor(new NameResolver); // we will need resolved names +$traverser->addVisitor(new NamespaceConverter); // our own node visitor + +// iterate over all .php files in the directory +$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($inDir)); +$files = new \RegexIterator($files, '/\.php$/'); + +foreach ($files as $file) { + try { + // read the file that should be converted + $code = file_get_contents($file); + + // parse + $stmts = $parser->parse($code); + + // traverse + $stmts = $traverser->traverse($stmts); + + // pretty print + $code = $prettyPrinter->prettyPrintFile($stmts); + + // write the converted file to the target directory + file_put_contents( + substr_replace($file->getPathname(), $outDir, 0, strlen($inDir)), + $code + ); + } catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); + } +} +``` + +Now lets start with the main code, the `NodeVisitor\NamespaceConverter`. One thing it needs to do +is convert `A\\B` style names to `A_B` style ones. + +```php +use PhpParser\Node; + +class NamespaceConverter extends \PhpParser\NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Name) { + return new Node\Name($node->toString('_')); + } + } +} +``` + +The above code profits from the fact that the `NameResolver` already resolved all names as far as +possible, so we don't need to do that. We only need to create a string with the name parts separated +by underscores instead of backslashes. This is what `$node->toString('_')` does. (If you want to +create a name with backslashes either write `$node->toString()` or `(string) $node`.) Then we create +a new name from the string and return it. Returning a new node replaces the old node. + +Another thing we need to do is change the class/function/const declarations. Currently they contain +only the shortname (i.e. the last part of the name), but they need to contain the complete name including +the namespace prefix: + +```php +use PhpParser\Node; +use PhpParser\Node\Stmt; + +class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Name) { + return new Node\Name($node->toString('_')); + } elseif ($node instanceof Stmt\Class_ + || $node instanceof Stmt\Interface_ + || $node instanceof Stmt\Function_) { + $node->name = $node->namespacedName->toString('_'); + } elseif ($node instanceof Stmt\Const_) { + foreach ($node->consts as $const) { + $const->name = $const->namespacedName->toString('_'); + } + } + } +} +``` + +There is not much more to it than converting the namespaced name to string with `_` as separator. + +The last thing we need to do is remove the `namespace` and `use` statements: + +```php +use PhpParser\Node; +use PhpParser\Node\Stmt; + +class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Name) { + return new Node\Name($node->toString('_')); + } elseif ($node instanceof Stmt\Class_ + || $node instanceof Stmt\Interface_ + || $node instanceof Stmt\Function_) { + $node->name = $node->namespacedName->toString('_'); + } elseif ($node instanceof Stmt\Const_) { + foreach ($node->consts as $const) { + $const->name = $const->namespacedName->toString('_'); + } + } elseif ($node instanceof Stmt\Namespace_) { + // returning an array merges is into the parent array + return $node->stmts; + } elseif ($node instanceof Stmt\Use_) { + // returning false removed the node altogether + return false; + } + } +} +``` + +That's all. diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/3_Other_node_tree_representations.markdown b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/3_Other_node_tree_representations.markdown new file mode 100644 index 00000000000..691640e604d --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/3_Other_node_tree_representations.markdown @@ -0,0 +1,202 @@ +Other node tree representations +=============================== + +It is possible to convert the AST into several textual representations, which serve different uses. + +Simple serialization +-------------------- + +It is possible to serialize the node tree using `serialize()` and also unserialize it using +`unserialize()`. The output is not human readable and not easily processable from anything +but PHP, but it is compact and generates fast. The main application thus is in caching. + +Human readable dumping +---------------------- + +Furthermore it is possible to dump nodes into a human readable format using the `dump` method of +`PhpParser\NodeDumper`. This can be used for debugging. + +```php +$code = <<<'CODE' +create(PhpParser\ParserFactory::PREFER_PHP7); +$nodeDumper = new PhpParser\NodeDumper; + +try { + $stmts = $parser->parse($code); + + echo $nodeDumper->dump($stmts), "\n"; +} catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +The above script will have an output looking roughly like this: + +``` +array( + 0: Stmt_Function( + byRef: false + params: array( + 0: Param( + name: msg + default: null + type: null + byRef: false + ) + ) + stmts: array( + 0: Stmt_Echo( + exprs: array( + 0: Expr_Variable( + name: msg + ) + 1: Scalar_String( + value: + + ) + ) + ) + ) + name: printLine + ) + 1: Expr_FuncCall( + name: Name( + parts: array( + 0: printLine + ) + ) + args: array( + 0: Arg( + value: Scalar_String( + value: Hello World!!! + ) + byRef: false + ) + ) + ) +) +``` + +Serialization to XML +-------------------- + +It is also possible to serialize the node tree to XML using `PhpParser\Serializer\XML->serialize()` +and to unserialize it using `PhpParser\Unserializer\XML->unserialize()`. This is useful for +interfacing with other languages and applications or for doing transformation using XSLT. + +```php +create(PhpParser\ParserFactory::PREFER_PHP7); +$serializer = new PhpParser\Serializer\XML; + +try { + $stmts = $parser->parse($code); + + echo $serializer->serialize($stmts); +} catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +Produces: + +```xml + + + + + + + + + + + + msg + + + + + + + + + + + + + + + + + + + + + msg + + + + + + + + + + + + + + + printLine + + + + + + + + printLine + + + + + + + + + + + Hello World!!! + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/4_Code_generation.markdown b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/4_Code_generation.markdown new file mode 100644 index 00000000000..c8322445f21 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/4_Code_generation.markdown @@ -0,0 +1,83 @@ +Code generation +=============== + +It is also possible to generate code using the parser, by first creating an Abstract Syntax Tree and then using the +pretty printer to convert it to PHP code. To simplify code generation, the project comes with builders which allow +creating node trees using a fluid interface, instead of instantiating all nodes manually. Builders are available for +the following syntactic elements: + + * namespaces and use statements + * classes, interfaces and traits + * methods, functions and parameters + * properties + +Here is an example: + +```php +use PhpParser\BuilderFactory; +use PhpParser\PrettyPrinter; +use PhpParser\Node; + +$factory = new BuilderFactory; +$node = $factory->namespace('Name\Space') + ->addStmt($factory->use('Some\Other\Thingy')->as('SomeOtherClass')) + ->addStmt($factory->class('SomeClass') + ->extend('SomeOtherClass') + ->implement('A\Few', '\Interfaces') + ->makeAbstract() // ->makeFinal() + + ->addStmt($factory->method('someMethod') + ->makePublic() + ->makeAbstract() // ->makeFinal() + ->addParam($factory->param('someParam')->setTypeHint('SomeClass')) + ->setDocComment('/** + * This method does something. + * + * @param SomeClass And takes a parameter + */') + ) + + ->addStmt($factory->method('anotherMethod') + ->makeProtected() // ->makePublic() [default], ->makePrivate() + ->addParam($factory->param('someParam')->setDefault('test')) + // it is possible to add manually created nodes + ->addStmt(new Node\Expr\Print_(new Node\Expr\Variable('someParam'))) + ) + + // properties will be correctly reordered above the methods + ->addStmt($factory->property('someProperty')->makeProtected()) + ->addStmt($factory->property('anotherProperty')->makePrivate()->setDefault(array(1, 2, 3))) + ) + + ->getNode() +; + +$stmts = array($node); +$prettyPrinter = new PrettyPrinter\Standard(); +echo $prettyPrinter->prettyPrintFile($stmts); +``` + +This will produce the following output with the standard pretty printer: + +```php + array('comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'), +)); +$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer); + +try { + $stmts = $parser->parse($code); + // ... +} catch (PhpParser\Error $e) { + // ... +} +``` + +Before using column information its availability needs to be checked with `$e->hasColumnInfo()`, as the precise +location of an error cannot always be determined. The methods for retrieving column information also have to be passed +the source code of the parsed file. An example for printing an error: + +```php +if ($e->hasColumnInfo()) { + echo $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code) + . ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code); +} else { + echo $e->getMessage(); +} +``` + +Both line numbers and column numbers are 1-based. EOF errors will be located at the position one past the end of the +file. + +Error recovery +-------------- + +> **EXPERIMENTAL** + +By default the parser will throw an exception upon encountering the first error during parsing. An alternative mode is +also supported, in which the parser will remember the error, but try to continue parsing the rest of the source code. + +To enable this mode the `throwOnError` parser option needs to be disabled. Any errors that occurred during parsing can +then be retrieved using `$parser->getErrors()`. The `$parser->parse()` method will either return a partial syntax tree +or `null` if recovery fails. + +A usage example: + +```php +$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, null, array( + 'throwOnError' => false, +)); + +$stmts = $parser->parse($code); +$errors = $parser->getErrors(); + +foreach ($errors as $error) { + // $error is an ordinary PhpParser\Error +} + +if (null !== $stmts) { + // $stmts is a best-effort partial AST +} +``` + +The error recovery implementation is experimental -- it currently won't be able to recover from many types of errors. diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/component/Lexer.markdown b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/component/Lexer.markdown new file mode 100644 index 00000000000..422dd378f52 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/doc/component/Lexer.markdown @@ -0,0 +1,149 @@ +Lexer component documentation +============================= + +The lexer is responsible for providing tokens to the parser. The project comes with two lexers: `PhpParser\Lexer` and +`PhpParser\Lexer\Emulative`. The latter is an extension of the former, which adds the ability to emulate tokens of +newer PHP versions and thus allows parsing of new code on older versions. + +This documentation discusses options available for the default lexers and explains how lexers can be extended. + +Lexer options +------------- + +The two default lexers accept an `$options` array in the constructor. Currently only the `'usedAttributes'` option is +supported, which allows you to specify which attributes will be added to the AST nodes. The attributes can then be +accessed using `$node->getAttribute()`, `$node->setAttribute()`, `$node->hasAttribute()` and `$node->getAttributes()` +methods. A sample options array: + +```php +$lexer = new PhpParser\Lexer(array( + 'usedAttributes' => array( + 'comments', 'startLine', 'endLine' + ) +)); +``` + +The attributes used in this example match the default behavior of the lexer. The following attributes are supported: + + * `comments`: Array of `PhpParser\Comment` or `PhpParser\Comment\Doc` instances, representing all comments that occurred + between the previous non-discarded token and the current one. Use of this attribute is required for the + `$node->getDocComment()` method to work. The attribute is also needed if you wish the pretty printer to retain + comments present in the original code. + * `startLine`: Line in which the node starts. This attribute is required for the `$node->getLine()` to work. It is also + required if syntax errors should contain line number information. + * `endLine`: Line in which the node ends. + * `startTokenPos`: Offset into the token array of the first token in the node. + * `endTokenPos`: Offset into the token array of the last token in the node. + * `startFilePos`: Offset into the code string of the first character that is part of the node. + * `endFilePos`: Offset into the code string of the last character that is part of the node. + +### Using token positions + +The token offset information is useful if you wish to examine the exact formatting used for a node. For example the AST +does not distinguish whether a property was declared using `public` or using `var`, but you can retrieve this +information based on the token position: + +```php +function isDeclaredUsingVar(array $tokens, PhpParser\Node\Stmt\Property $prop) { + $i = $prop->getAttribute('startTokenPos'); + return $tokens[$i][0] === T_VAR; +} +``` + +In order to make use of this function, you will have to provide the tokens from the lexer to your node visitor using +code similar to the following: + +```php +class MyNodeVisitor extends PhpParser\NodeVisitorAbstract { + private $tokens; + public function setTokens(array $tokens) { + $this->tokens = $tokens; + } + + public function leaveNode(PhpParser\Node $node) { + if ($node instanceof PhpParser\Node\Stmt\Property) { + var_dump(isDeclaredUsingVar($this->tokens, $node)); + } + } +} + +$lexer = new PhpParser\Lexer(array( + 'usedAttributes' => array( + 'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos' + ) +)); +$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer); + +$visitor = new MyNodeVisitor(); +$traverser = new PhpParser\NodeTraverser(); +$traverser->addVisitor($visitor); + +try { + $stmts = $parser->parse($code); + $visitor->setTokens($lexer->getTokens()); + $stmts = $traverser->traverse($stmts); +} catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +The same approach can also be used to perform specific modifications in the code, without changing the formatting in +other places (which is the case when using the pretty printer). + +Lexer extension +--------------- + +A lexer has to define the following public interface: + + void startLexing(string $code); + array getTokens(); + string handleHaltCompiler(); + int getNextToken(string &$value = null, array &$startAttributes = null, array &$endAttributes = null); + +The `startLexing()` method is invoked with the source code that is to be lexed (including the opening tag) whenever the +`parse()` method of the parser is called. It can be used to reset state or preprocess the source code or tokens. + +The `getTokens()` method returns the current token array, in the usual `token_get_all()` format. This method is not +used by the parser (which uses `getNextToken()`), but is useful in combination with the token position attributes. + +The `handleHaltCompiler()` method is called whenever a `T_HALT_COMPILER` token is encountered. It has to return the +remaining string after the construct (not including `();`). + +The `getNextToken()` method returns the ID of the next token (as defined by the `Parser::T_*` constants). If no more +tokens are available it must return `0`, which is the ID of the `EOF` token. Furthermore the string content of the +token should be written into the by-reference `$value` parameter (which will then be available as `$n` in the parser). + +### Attribute handling + +The other two by-ref variables `$startAttributes` and `$endAttributes` define which attributes will eventually be +assigned to the generated nodes: The parser will take the `$startAttributes` from the first token which is part of the +node and the `$endAttributes` from the last token that is part of the node. + +E.g. if the tokens `T_FUNCTION T_STRING ... '{' ... '}'` constitute a node, then the `$startAttributes` from the +`T_FUNCTION` token will be taken and the `$endAttributes` from the `'}'` token. + +An application of custom attributes is storing the original formatting of literals: The parser does not retain +information about the formatting of integers (like decimal vs. hexadecimal) or strings (like used quote type or used +escape sequences). This can be remedied by storing the original value in an attribute: + +```php +use PhpParser\Lexer; +use PhpParser\Parser\Tokens; + +class KeepOriginalValueLexer extends Lexer // or Lexer\Emulative +{ + public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) { + $tokenId = parent::getNextToken($value, $startAttributes, $endAttributes); + + if ($tokenId == Tokens::T_CONSTANT_ENCAPSED_STRING // non-interpolated string + || $tokenId == Tokens::T_LNUMBER // integer + || $tokenId == Tokens::T_DNUMBER // floating point number + ) { + // could also use $startAttributes, doesn't really matter here + $endAttributes['originalValue'] = $value; + } + + return $tokenId; + } +} +``` diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/README.md b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/README.md new file mode 100644 index 00000000000..93206a8f277 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/README.md @@ -0,0 +1,29 @@ +What do all those files mean? +============================= + + * `php5.y`: PHP 5 grammar written in a pseudo language + * `php7.y`: PHP 7 grammar written in a pseudo language + * `tokens.y`: Tokens definition shared between PHP 5 and PHP 7 grammars + * `parser.template`: A `kmyacc` parser prototype file for PHP + * `tokens.template`: A `kmyacc` prototype file for the `Tokens` class + * `analyze.php`: Analyzes the grammer and outputs some info about it + * `rebuildParser.php`: Preprocesses the grammar and builds the parser using `kmyacc` + +.phpy pseudo language +===================== + +The `.y` file is a normal grammer in `kmyacc` (`yacc`) style, with some transformations +applied to it: + + * Nodes are created using the syntax `Name[..., ...]`. This is transformed into + `new Name(..., ..., attributes())` + * Some function-like constructs are resolved (see `rebuildParser.php` for a list) + +Building the parser +=================== + +In order to rebuild the parser, you need [moriyoshi's fork of kmyacc](https://github.com/moriyoshi/kmyacc-forked). +After you compiled/installed it, run the `rebuildParser.php` script. + +By default only the `Parser.php` is built. If you want to additionally emit debug symbols and create `y.output`, run the +script with `--debug`. If you want to retain the preprocessed grammar pass `--keep-tmp-grammar`. diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/analyze.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/analyze.php new file mode 100644 index 00000000000..cd30b3c961b --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/analyze.php @@ -0,0 +1,96 @@ +\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\') + (?"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+") + (?(?&singleQuotedString)|(?&doubleQuotedString)) + (?/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/) + (?\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+}) +)'; + +const RULE_BLOCK = '(?[a-z_]++):(?[^\'"/{};]*+(?:(?:(?&string)|(?&comment)|(?&code)|/|})[^\'"/{};]*+)*+);'; + +$usedTerminals = array_flip(array( + 'T_VARIABLE', 'T_STRING', 'T_INLINE_HTML', 'T_ENCAPSED_AND_WHITESPACE', + 'T_LNUMBER', 'T_DNUMBER', 'T_CONSTANT_ENCAPSED_STRING', 'T_STRING_VARNAME', 'T_NUM_STRING' +)); +$unusedNonterminals = array_flip(array( + 'case_separator', 'optional_comma' +)); + +function regex($regex) { + return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~'; +} + +function magicSplit($regex, $string) { + $pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string); + + foreach ($pieces as &$piece) { + $piece = trim($piece); + } + + return array_filter($pieces); +} + +echo '
';
+
+////////////////////
+////////////////////
+////////////////////
+
+list($defs, $ruleBlocks) = magicSplit('%%', file_get_contents(GRAMMAR_FILE));
+
+if ('' !== trim(preg_replace(regex(RULE_BLOCK), '', $ruleBlocks))) {
+    die('Not all rule blocks were properly recognized!');
+}
+
+preg_match_all(regex(RULE_BLOCK), $ruleBlocks, $ruleBlocksMatches, PREG_SET_ORDER);
+foreach ($ruleBlocksMatches as $match) {
+    $ruleBlockName = $match['name'];
+    $rules = magicSplit('\|', $match['rules']);
+
+    foreach ($rules as &$rule) {
+        $parts = magicSplit('\s+', $rule);
+        $usedParts = array();
+
+        foreach ($parts as $part) {
+            if ('{' === $part[0]) {
+                preg_match_all('~\$([0-9]+)~', $part, $backReferencesMatches, PREG_SET_ORDER);
+                foreach ($backReferencesMatches as $match) {
+                    $usedParts[$match[1]] = true;
+                }
+            }
+        }
+
+        $i = 1;
+        foreach ($parts as &$part) {
+            if ('/' === $part[0]) {
+                continue;
+            }
+
+            if (isset($usedParts[$i])) {
+                if ('\'' === $part[0] || '{' === $part[0]
+                    || (ctype_upper($part[0]) && !isset($usedTerminals[$part]))
+                    || (ctype_lower($part[0]) && isset($unusedNonterminals[$part]))
+                ) {
+                    $part = '' . $part . '';
+                } else {
+                    $part = '' . $part . '';
+                }
+            } elseif ((ctype_upper($part[0]) && isset($usedTerminals[$part]))
+                      || (ctype_lower($part[0]) && !isset($unusedNonterminals[$part]))
+
+            ) {
+                $part = '' . $part . '';
+            }
+
+            ++$i;
+        }
+
+        $rule = implode(' ', $parts);
+    }
+
+    echo $ruleBlockName, ':', "\n", '      ', implode("\n" . '    | ', $rules), "\n", ';', "\n\n";
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/parser.template b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/parser.template
new file mode 100644
index 00000000000..fff893ff42c
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/parser.template
@@ -0,0 +1,103 @@
+semValue
+#semval($,%t) $this->semValue
+#semval(%n) $this->stackPos-(%l-%n)
+#semval(%n,%t) $this->stackPos-(%l-%n)
+
+namespace PhpParser\Parser;
+
+use PhpParser\Error;
+use PhpParser\Node;
+use PhpParser\Node\Expr;
+use PhpParser\Node\Name;
+use PhpParser\Node\Scalar;
+use PhpParser\Node\Stmt;
+#include;
+
+/* This is an automatically GENERATED file, which should not be manually edited.
+ * Instead edit one of the following:
+ *  * the grammar files grammar/php5.y or grammar/php7.y
+ *  * the skeleton file grammar/parser.template
+ *  * the preprocessing script grammar/rebuildParsers.php
+ */
+class #(-p) extends \PhpParser\ParserAbstract
+{
+    protected $tokenToSymbolMapSize = #(YYMAXLEX);
+    protected $actionTableSize = #(YYLAST);
+    protected $gotoTableSize = #(YYGLAST);
+
+    protected $invalidSymbol = #(YYBADCH);
+    protected $errorSymbol = #(YYINTERRTOK);
+    protected $defaultAction = #(YYDEFAULT);
+    protected $unexpectedTokenRule = #(YYUNEXPECTED);
+
+    protected $YY2TBLSTATE  = #(YY2TBLSTATE);
+    protected $YYNLSTATES   = #(YYNLSTATES);
+
+    protected $symbolToName = array(
+        #listvar terminals
+    );
+
+    protected $tokenToSymbol = array(
+        #listvar yytranslate
+    );
+
+    protected $action = array(
+        #listvar yyaction
+    );
+
+    protected $actionCheck = array(
+        #listvar yycheck
+    );
+
+    protected $actionBase = array(
+        #listvar yybase
+    );
+
+    protected $actionDefault = array(
+        #listvar yydefault
+    );
+
+    protected $goto = array(
+        #listvar yygoto
+    );
+
+    protected $gotoCheck = array(
+        #listvar yygcheck
+    );
+
+    protected $gotoBase = array(
+        #listvar yygbase
+    );
+
+    protected $gotoDefault = array(
+        #listvar yygdefault
+    );
+
+    protected $ruleToNonTerminal = array(
+        #listvar yylhs
+    );
+
+    protected $ruleToLength = array(
+        #listvar yylen
+    );
+#if -t
+
+    protected $productions = array(
+        #production-strings;
+    );
+#endif
+#reduce
+
+    protected function reduceRule%n() {
+        %b
+    }
+#noact
+
+    protected function reduceRule%n() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+#endreduce
+}
+#tailcode;
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/php5.y b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/php5.y
new file mode 100644
index 00000000000..9bd7df27ef5
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/php5.y
@@ -0,0 +1,951 @@
+%pure_parser
+%expect 6
+
+%tokens
+
+%%
+
+start:
+    top_statement_list                                      { $$ = $this->handleNamespaces($1); }
+;
+
+top_statement_list:
+      top_statement_list top_statement                      { pushNormalizing($1, $2); }
+    | /* empty */                                           { init(); }
+;
+
+reserved_non_modifiers:
+      T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
+    | T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
+    | T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH
+    | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
+    | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
+    | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
+    | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
+;
+
+semi_reserved:
+      reserved_non_modifiers
+    | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC
+;
+
+identifier:
+      T_STRING                                              { $$ = $1; }
+    | semi_reserved                                         { $$ = $1; }
+;
+
+namespace_name_parts:
+      T_STRING                                              { init($1); }
+    | namespace_name_parts T_NS_SEPARATOR T_STRING          { push($1, $3); }
+;
+
+namespace_name:
+      namespace_name_parts                                  { $$ = Name[$1]; }
+;
+
+top_statement:
+      statement                                             { $$ = $1; }
+    | function_declaration_statement                        { $$ = $1; }
+    | class_declaration_statement                           { $$ = $1; }
+    | T_HALT_COMPILER
+          { $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; }
+    | T_NAMESPACE namespace_name ';'                        { $$ = Stmt\Namespace_[$2, null]; }
+    | T_NAMESPACE namespace_name '{' top_statement_list '}' { $$ = Stmt\Namespace_[$2, $4]; }
+    | T_NAMESPACE '{' top_statement_list '}'                { $$ = Stmt\Namespace_[null,     $3]; }
+    | T_USE use_declarations ';'                            { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
+    | T_USE use_type use_declarations ';'                   { $$ = Stmt\Use_[$3, $2]; }
+    | group_use_declaration ';'                             { $$ = $1; }
+    | T_CONST constant_declaration_list ';'                 { $$ = Stmt\Const_[$2]; }
+;
+
+use_type:
+      T_FUNCTION                                            { $$ = Stmt\Use_::TYPE_FUNCTION; }
+    | T_CONST                                               { $$ = Stmt\Use_::TYPE_CONSTANT; }
+;
+
+/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */
+group_use_declaration:
+      T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$3], $6, $2]; }
+    | T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$4], $7, $2]; }
+    | T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$2], $5, Stmt\Use_::TYPE_UNKNOWN]; }
+    | T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$3], $6, Stmt\Use_::TYPE_UNKNOWN]; }
+;
+
+unprefixed_use_declarations:
+      unprefixed_use_declarations ',' unprefixed_use_declaration
+          { push($1, $3); }
+    | unprefixed_use_declaration                            { init($1); }
+;
+
+use_declarations:
+      use_declarations ',' use_declaration                  { push($1, $3); }
+    | use_declaration                                       { init($1); }
+;
+
+inline_use_declarations:
+      inline_use_declarations ',' inline_use_declaration    { push($1, $3); }
+    | inline_use_declaration                                { init($1); }
+;
+
+unprefixed_use_declaration:
+      namespace_name                                        { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; }
+    | namespace_name T_AS T_STRING                          { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; }
+;
+
+use_declaration:
+      unprefixed_use_declaration                            { $$ = $1; }
+    | T_NS_SEPARATOR unprefixed_use_declaration             { $$ = $2; }
+;
+
+inline_use_declaration:
+      unprefixed_use_declaration                            { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; }
+    | use_type unprefixed_use_declaration                   { $$ = $2; $$->type = $1; }
+;
+
+constant_declaration_list:
+      constant_declaration_list ',' constant_declaration    { push($1, $3); }
+    | constant_declaration                                  { init($1); }
+;
+
+constant_declaration:
+    T_STRING '=' static_scalar                              { $$ = Node\Const_[$1, $3]; }
+;
+
+class_const_list:
+      class_const_list ',' class_const                      { push($1, $3); }
+    | class_const                                           { init($1); }
+;
+
+class_const:
+    identifier '=' static_scalar                            { $$ = Node\Const_[$1, $3]; }
+;
+
+inner_statement_list:
+      inner_statement_list inner_statement                  { pushNormalizing($1, $2); }
+    | /* empty */                                           { init(); }
+;
+
+inner_statement:
+      statement                                             { $$ = $1; }
+    | function_declaration_statement                        { $$ = $1; }
+    | class_declaration_statement                           { $$ = $1; }
+    | T_HALT_COMPILER
+          { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
+;
+
+non_empty_statement:
+      '{' inner_statement_list '}'                          { $$ = $2; }
+    | T_IF parentheses_expr statement elseif_list else_single
+          { $$ = Stmt\If_[$2, ['stmts' => toArray($3), 'elseifs' => $4, 'else' => $5]]; }
+    | T_IF parentheses_expr ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
+          { $$ = Stmt\If_[$2, ['stmts' => $4, 'elseifs' => $5, 'else' => $6]]; }
+    | T_WHILE parentheses_expr while_statement              { $$ = Stmt\While_[$2, $3]; }
+    | T_DO statement T_WHILE parentheses_expr ';'           { $$ = Stmt\Do_   [$4, toArray($2)]; }
+    | T_FOR '(' for_expr ';'  for_expr ';' for_expr ')' for_statement
+          { $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; }
+    | T_SWITCH parentheses_expr switch_case_list            { $$ = Stmt\Switch_[$2, $3]; }
+    | T_BREAK ';'                                           { $$ = Stmt\Break_[null]; }
+    | T_BREAK expr ';'                                      { $$ = Stmt\Break_[$2]; }
+    | T_CONTINUE ';'                                        { $$ = Stmt\Continue_[null]; }
+    | T_CONTINUE expr ';'                                   { $$ = Stmt\Continue_[$2]; }
+    | T_RETURN ';'                                          { $$ = Stmt\Return_[null]; }
+    | T_RETURN expr ';'                                     { $$ = Stmt\Return_[$2]; }
+    | yield_expr ';'                                        { $$ = $1; }
+    | T_GLOBAL global_var_list ';'                          { $$ = Stmt\Global_[$2]; }
+    | T_STATIC static_var_list ';'                          { $$ = Stmt\Static_[$2]; }
+    | T_ECHO expr_list ';'                                  { $$ = Stmt\Echo_[$2]; }
+    | T_INLINE_HTML                                         { $$ = Stmt\InlineHTML[$1]; }
+    | expr ';'                                              { $$ = $1; }
+    | T_UNSET '(' variables_list ')' ';'                    { $$ = Stmt\Unset_[$3]; }
+    | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
+          { $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; }
+    | T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement
+          { $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
+    | T_DECLARE '(' declare_list ')' declare_statement      { $$ = Stmt\Declare_[$3, $5]; }
+    | T_TRY '{' inner_statement_list '}' catches optional_finally
+          { $$ = Stmt\TryCatch[$3, $5, $6]; }
+    | T_THROW expr ';'                                      { $$ = Stmt\Throw_[$2]; }
+    | T_GOTO T_STRING ';'                                   { $$ = Stmt\Goto_[$2]; }
+    | T_STRING ':'                                          { $$ = Stmt\Label[$1]; }
+    | error                                                 { $$ = array(); /* means: no statement */ }
+;
+
+statement:
+      non_empty_statement                                   { $$ = $1; }
+    | ';'                                                   { $$ = array(); /* means: no statement */ }
+;
+
+catches:
+      /* empty */                                           { init(); }
+    | catches catch                                         { push($1, $2); }
+;
+
+catch:
+    T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}'
+        { $$ = Stmt\Catch_[$3, parseVar($4), $7]; }
+;
+
+optional_finally:
+      /* empty */                                           { $$ = null; }
+    | T_FINALLY '{' inner_statement_list '}'                { $$ = $3; }
+;
+
+variables_list:
+      variable                                              { init($1); }
+    | variables_list ',' variable                           { push($1, $3); }
+;
+
+optional_ref:
+      /* empty */                                           { $$ = false; }
+    | '&'                                                   { $$ = true; }
+;
+
+optional_ellipsis:
+      /* empty */                                           { $$ = false; }
+    | T_ELLIPSIS                                            { $$ = true; }
+;
+
+function_declaration_statement:
+    T_FUNCTION optional_ref T_STRING '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
+        { $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
+;
+
+class_declaration_statement:
+      class_entry_type T_STRING extends_from implements_list '{' class_statement_list '}'
+          { $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; }
+    | T_INTERFACE T_STRING interface_extends_list '{' class_statement_list '}'
+          { $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; }
+    | T_TRAIT T_STRING '{' class_statement_list '}'
+          { $$ = Stmt\Trait_[$2, $4]; }
+;
+
+class_entry_type:
+      T_CLASS                                               { $$ = 0; }
+    | T_ABSTRACT T_CLASS                                    { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
+    | T_FINAL T_CLASS                                       { $$ = Stmt\Class_::MODIFIER_FINAL; }
+;
+
+extends_from:
+      /* empty */                                           { $$ = null; }
+    | T_EXTENDS name                                        { $$ = $2; }
+;
+
+interface_extends_list:
+      /* empty */                                           { $$ = array(); }
+    | T_EXTENDS name_list                                   { $$ = $2; }
+;
+
+implements_list:
+      /* empty */                                           { $$ = array(); }
+    | T_IMPLEMENTS name_list                                { $$ = $2; }
+;
+
+name_list:
+      name                                                  { init($1); }
+    | name_list ',' name                                    { push($1, $3); }
+;
+
+for_statement:
+      statement                                             { $$ = toArray($1); }
+    | ':' inner_statement_list T_ENDFOR ';'                 { $$ = $2; }
+;
+
+foreach_statement:
+      statement                                             { $$ = toArray($1); }
+    | ':' inner_statement_list T_ENDFOREACH ';'             { $$ = $2; }
+;
+
+declare_statement:
+      non_empty_statement                                   { $$ = toArray($1); }
+    | ';'                                                   { $$ = null; }
+    | ':' inner_statement_list T_ENDDECLARE ';'             { $$ = $2; }
+;
+
+declare_list:
+      declare_list_element                                  { init($1); }
+    | declare_list ',' declare_list_element                 { push($1, $3); }
+;
+
+declare_list_element:
+      T_STRING '=' static_scalar                            { $$ = Stmt\DeclareDeclare[$1, $3]; }
+;
+
+switch_case_list:
+      '{' case_list '}'                                     { $$ = $2; }
+    | '{' ';' case_list '}'                                 { $$ = $3; }
+    | ':' case_list T_ENDSWITCH ';'                         { $$ = $2; }
+    | ':' ';' case_list T_ENDSWITCH ';'                     { $$ = $3; }
+;
+
+case_list:
+      /* empty */                                           { init(); }
+    | case_list case                                        { push($1, $2); }
+;
+
+case:
+      T_CASE expr case_separator inner_statement_list       { $$ = Stmt\Case_[$2, $4]; }
+    | T_DEFAULT case_separator inner_statement_list         { $$ = Stmt\Case_[null, $3]; }
+;
+
+case_separator:
+      ':'
+    | ';'
+;
+
+while_statement:
+      statement                                             { $$ = toArray($1); }
+    | ':' inner_statement_list T_ENDWHILE ';'               { $$ = $2; }
+;
+
+elseif_list:
+      /* empty */                                           { init(); }
+    | elseif_list elseif                                    { push($1, $2); }
+;
+
+elseif:
+      T_ELSEIF parentheses_expr statement                   { $$ = Stmt\ElseIf_[$2, toArray($3)]; }
+;
+
+new_elseif_list:
+      /* empty */                                           { init(); }
+    | new_elseif_list new_elseif                            { push($1, $2); }
+;
+
+new_elseif:
+     T_ELSEIF parentheses_expr ':' inner_statement_list     { $$ = Stmt\ElseIf_[$2, $4]; }
+;
+
+else_single:
+      /* empty */                                           { $$ = null; }
+    | T_ELSE statement                                      { $$ = Stmt\Else_[toArray($2)]; }
+;
+
+new_else_single:
+      /* empty */                                           { $$ = null; }
+    | T_ELSE ':' inner_statement_list                       { $$ = Stmt\Else_[$3]; }
+;
+
+foreach_variable:
+      variable                                              { $$ = array($1, false); }
+    | '&' variable                                          { $$ = array($2, true); }
+    | list_expr                                             { $$ = array($1, false); }
+;
+
+parameter_list:
+      non_empty_parameter_list                              { $$ = $1; }
+    | /* empty */                                           { $$ = array(); }
+;
+
+non_empty_parameter_list:
+      parameter                                             { init($1); }
+    | non_empty_parameter_list ',' parameter                { push($1, $3); }
+;
+
+parameter:
+      optional_param_type optional_ref optional_ellipsis T_VARIABLE
+          { $$ = Node\Param[parseVar($4), null, $1, $2, $3]; }
+    | optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' static_scalar
+          { $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; }
+;
+
+type:
+      name                                                  { $$ = $1; }
+    | T_ARRAY                                               { $$ = 'array'; }
+    | T_CALLABLE                                            { $$ = 'callable'; }
+;
+
+optional_param_type:
+      /* empty */                                           { $$ = null; }
+    | type                                                  { $$ = $1; }
+;
+
+optional_return_type:
+      /* empty */                                           { $$ = null; }
+    | ':' type                                              { $$ = $2; }
+;
+
+argument_list:
+      '(' ')'                                               { $$ = array(); }
+    | '(' non_empty_argument_list ')'                       { $$ = $2; }
+    | '(' yield_expr ')'                                    { $$ = array(Node\Arg[$2, false, false]); }
+;
+
+non_empty_argument_list:
+      argument                                              { init($1); }
+    | non_empty_argument_list ',' argument                  { push($1, $3); }
+;
+
+argument:
+      expr                                                  { $$ = Node\Arg[$1, false, false]; }
+    | '&' variable                                          { $$ = Node\Arg[$2, true, false]; }
+    | T_ELLIPSIS expr                                       { $$ = Node\Arg[$2, false, true]; }
+;
+
+global_var_list:
+      global_var_list ',' global_var                        { push($1, $3); }
+    | global_var                                            { init($1); }
+;
+
+global_var:
+      T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
+    | '$' variable                                          { $$ = Expr\Variable[$2]; }
+    | '$' '{' expr '}'                                      { $$ = Expr\Variable[$3]; }
+;
+
+static_var_list:
+      static_var_list ',' static_var                        { push($1, $3); }
+    | static_var                                            { init($1); }
+;
+
+static_var:
+      T_VARIABLE                                            { $$ = Stmt\StaticVar[parseVar($1), null]; }
+    | T_VARIABLE '=' static_scalar                          { $$ = Stmt\StaticVar[parseVar($1), $3]; }
+;
+
+class_statement_list:
+      class_statement_list class_statement                  { push($1, $2); }
+    | /* empty */                                           { init(); }
+;
+
+class_statement:
+      variable_modifiers property_declaration_list ';'      { $$ = Stmt\Property[$1, $2]; }
+    | T_CONST class_const_list ';'                          { $$ = Stmt\ClassConst[$2]; }
+    | method_modifiers T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type method_body
+          { $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; }
+    | T_USE name_list trait_adaptations                     { $$ = Stmt\TraitUse[$2, $3]; }
+;
+
+trait_adaptations:
+      ';'                                                   { $$ = array(); }
+    | '{' trait_adaptation_list '}'                         { $$ = $2; }
+;
+
+trait_adaptation_list:
+      /* empty */                                           { init(); }
+    | trait_adaptation_list trait_adaptation                { push($1, $2); }
+;
+
+trait_adaptation:
+      trait_method_reference_fully_qualified T_INSTEADOF name_list ';'
+          { $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
+    | trait_method_reference T_AS member_modifier identifier ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
+    | trait_method_reference T_AS member_modifier ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
+    | trait_method_reference T_AS T_STRING ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
+    | trait_method_reference T_AS reserved_non_modifiers ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
+;
+
+trait_method_reference_fully_qualified:
+      name T_PAAMAYIM_NEKUDOTAYIM identifier                { $$ = array($1, $3); }
+;
+trait_method_reference:
+      trait_method_reference_fully_qualified                { $$ = $1; }
+    | identifier                                            { $$ = array(null, $1); }
+;
+
+method_body:
+      ';' /* abstract method */                             { $$ = null; }
+    | '{' inner_statement_list '}'                          { $$ = $2; }
+;
+
+variable_modifiers:
+      non_empty_member_modifiers                            { $$ = $1; }
+    | T_VAR                                                 { $$ = 0; }
+;
+
+method_modifiers:
+      /* empty */                                           { $$ = 0; }
+    | non_empty_member_modifiers                            { $$ = $1; }
+;
+
+non_empty_member_modifiers:
+      member_modifier                                       { $$ = $1; }
+    | non_empty_member_modifiers member_modifier            { Stmt\Class_::verifyModifier($1, $2); $$ = $1 | $2; }
+;
+
+member_modifier:
+      T_PUBLIC                                              { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
+    | T_PROTECTED                                           { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
+    | T_PRIVATE                                             { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
+    | T_STATIC                                              { $$ = Stmt\Class_::MODIFIER_STATIC; }
+    | T_ABSTRACT                                            { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
+    | T_FINAL                                               { $$ = Stmt\Class_::MODIFIER_FINAL; }
+;
+
+property_declaration_list:
+      property_declaration                                  { init($1); }
+    | property_declaration_list ',' property_declaration    { push($1, $3); }
+;
+
+property_declaration:
+      T_VARIABLE                                            { $$ = Stmt\PropertyProperty[parseVar($1), null]; }
+    | T_VARIABLE '=' static_scalar                          { $$ = Stmt\PropertyProperty[parseVar($1), $3]; }
+;
+
+expr_list:
+      expr_list ',' expr                                    { push($1, $3); }
+    | expr                                                  { init($1); }
+;
+
+for_expr:
+      /* empty */                                           { $$ = array(); }
+    | expr_list                                             { $$ = $1; }
+;
+
+expr:
+      variable                                              { $$ = $1; }
+    | list_expr '=' expr                                    { $$ = Expr\Assign[$1, $3]; }
+    | variable '=' expr                                     { $$ = Expr\Assign[$1, $3]; }
+    | variable '=' '&' variable                             { $$ = Expr\AssignRef[$1, $4]; }
+    | variable '=' '&' new_expr                             { $$ = Expr\AssignRef[$1, $4]; }
+    | new_expr                                              { $$ = $1; }
+    | T_CLONE expr                                          { $$ = Expr\Clone_[$2]; }
+    | variable T_PLUS_EQUAL expr                            { $$ = Expr\AssignOp\Plus      [$1, $3]; }
+    | variable T_MINUS_EQUAL expr                           { $$ = Expr\AssignOp\Minus     [$1, $3]; }
+    | variable T_MUL_EQUAL expr                             { $$ = Expr\AssignOp\Mul       [$1, $3]; }
+    | variable T_DIV_EQUAL expr                             { $$ = Expr\AssignOp\Div       [$1, $3]; }
+    | variable T_CONCAT_EQUAL expr                          { $$ = Expr\AssignOp\Concat    [$1, $3]; }
+    | variable T_MOD_EQUAL expr                             { $$ = Expr\AssignOp\Mod       [$1, $3]; }
+    | variable T_AND_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseAnd[$1, $3]; }
+    | variable T_OR_EQUAL expr                              { $$ = Expr\AssignOp\BitwiseOr [$1, $3]; }
+    | variable T_XOR_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseXor[$1, $3]; }
+    | variable T_SL_EQUAL expr                              { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
+    | variable T_SR_EQUAL expr                              { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
+    | variable T_POW_EQUAL expr                             { $$ = Expr\AssignOp\Pow       [$1, $3]; }
+    | variable T_INC                                        { $$ = Expr\PostInc[$1]; }
+    | T_INC variable                                        { $$ = Expr\PreInc [$2]; }
+    | variable T_DEC                                        { $$ = Expr\PostDec[$1]; }
+    | T_DEC variable                                        { $$ = Expr\PreDec [$2]; }
+    | expr T_BOOLEAN_OR expr                                { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; }
+    | expr T_BOOLEAN_AND expr                               { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; }
+    | expr T_LOGICAL_OR expr                                { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; }
+    | expr T_LOGICAL_AND expr                               { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
+    | expr T_LOGICAL_XOR expr                               { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
+    | expr '|' expr                                         { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
+    | expr '&' expr                                         { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
+    | expr '^' expr                                         { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
+    | expr '.' expr                                         { $$ = Expr\BinaryOp\Concat    [$1, $3]; }
+    | expr '+' expr                                         { $$ = Expr\BinaryOp\Plus      [$1, $3]; }
+    | expr '-' expr                                         { $$ = Expr\BinaryOp\Minus     [$1, $3]; }
+    | expr '*' expr                                         { $$ = Expr\BinaryOp\Mul       [$1, $3]; }
+    | expr '/' expr                                         { $$ = Expr\BinaryOp\Div       [$1, $3]; }
+    | expr '%' expr                                         { $$ = Expr\BinaryOp\Mod       [$1, $3]; }
+    | expr T_SL expr                                        { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; }
+    | expr T_SR expr                                        { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; }
+    | expr T_POW expr                                       { $$ = Expr\BinaryOp\Pow       [$1, $3]; }
+    | '+' expr %prec T_INC                                  { $$ = Expr\UnaryPlus [$2]; }
+    | '-' expr %prec T_INC                                  { $$ = Expr\UnaryMinus[$2]; }
+    | '!' expr                                              { $$ = Expr\BooleanNot[$2]; }
+    | '~' expr                                              { $$ = Expr\BitwiseNot[$2]; }
+    | expr T_IS_IDENTICAL expr                              { $$ = Expr\BinaryOp\Identical     [$1, $3]; }
+    | expr T_IS_NOT_IDENTICAL expr                          { $$ = Expr\BinaryOp\NotIdentical  [$1, $3]; }
+    | expr T_IS_EQUAL expr                                  { $$ = Expr\BinaryOp\Equal         [$1, $3]; }
+    | expr T_IS_NOT_EQUAL expr                              { $$ = Expr\BinaryOp\NotEqual      [$1, $3]; }
+    | expr T_SPACESHIP expr                                 { $$ = Expr\BinaryOp\Spaceship     [$1, $3]; }
+    | expr '<' expr                                         { $$ = Expr\BinaryOp\Smaller       [$1, $3]; }
+    | expr T_IS_SMALLER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
+    | expr '>' expr                                         { $$ = Expr\BinaryOp\Greater       [$1, $3]; }
+    | expr T_IS_GREATER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
+    | expr T_INSTANCEOF class_name_reference                { $$ = Expr\Instanceof_[$1, $3]; }
+    | parentheses_expr                                      { $$ = $1; }
+    /* we need a separate '(' new_expr ')' rule to avoid problems caused by a s/r conflict */
+    | '(' new_expr ')'                                      { $$ = $2; }
+    | expr '?' expr ':' expr                                { $$ = Expr\Ternary[$1, $3,   $5]; }
+    | expr '?' ':' expr                                     { $$ = Expr\Ternary[$1, null, $4]; }
+    | expr T_COALESCE expr                                  { $$ = Expr\BinaryOp\Coalesce[$1, $3]; }
+    | T_ISSET '(' variables_list ')'                        { $$ = Expr\Isset_[$3]; }
+    | T_EMPTY '(' expr ')'                                  { $$ = Expr\Empty_[$3]; }
+    | T_INCLUDE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; }
+    | T_INCLUDE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; }
+    | T_EVAL parentheses_expr                               { $$ = Expr\Eval_[$2]; }
+    | T_REQUIRE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; }
+    | T_REQUIRE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; }
+    | T_INT_CAST expr                                       { $$ = Expr\Cast\Int_    [$2]; }
+    | T_DOUBLE_CAST expr                                    { $$ = Expr\Cast\Double  [$2]; }
+    | T_STRING_CAST expr                                    { $$ = Expr\Cast\String_ [$2]; }
+    | T_ARRAY_CAST expr                                     { $$ = Expr\Cast\Array_  [$2]; }
+    | T_OBJECT_CAST expr                                    { $$ = Expr\Cast\Object_ [$2]; }
+    | T_BOOL_CAST expr                                      { $$ = Expr\Cast\Bool_   [$2]; }
+    | T_UNSET_CAST expr                                     { $$ = Expr\Cast\Unset_  [$2]; }
+    | T_EXIT exit_expr                                      { $$ = Expr\Exit_        [$2]; }
+    | '@' expr                                              { $$ = Expr\ErrorSuppress[$2]; }
+    | scalar                                                { $$ = $1; }
+    | array_expr                                            { $$ = $1; }
+    | scalar_dereference                                    { $$ = $1; }
+    | '`' backticks_expr '`'                                { $$ = Expr\ShellExec[$2]; }
+    | T_PRINT expr                                          { $$ = Expr\Print_[$2]; }
+    | T_YIELD                                               { $$ = Expr\Yield_[null, null]; }
+    | T_YIELD_FROM expr                                     { $$ = Expr\YieldFrom[$2]; }
+    | T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
+      '{' inner_statement_list '}'
+          { $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $9]]; }
+    | T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
+      '{' inner_statement_list '}'
+          { $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $10]]; }
+;
+
+parentheses_expr:
+      '(' expr ')'                                          { $$ = $2; }
+    | '(' yield_expr ')'                                    { $$ = $2; }
+;
+
+yield_expr:
+      T_YIELD expr                                          { $$ = Expr\Yield_[$2, null]; }
+    | T_YIELD expr T_DOUBLE_ARROW expr                      { $$ = Expr\Yield_[$4, $2]; }
+;
+
+array_expr:
+      T_ARRAY '(' array_pair_list ')'                       { $$ = Expr\Array_[$3]; }
+    | '[' array_pair_list ']'                               { $$ = Expr\Array_[$2]; }
+;
+
+scalar_dereference:
+      array_expr '[' dim_offset ']'                         { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']'
+          { $$ = Expr\ArrayDimFetch[Scalar\String_[Scalar\String_::parse($1, false)], $3]; }
+    | constant '[' dim_offset ']'                           { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | scalar_dereference '[' dim_offset ']'                 { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    /* alternative array syntax missing intentionally */
+;
+
+anonymous_class:
+      T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
+          { $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); }
+
+new_expr:
+      T_NEW class_name_reference ctor_arguments             { $$ = Expr\New_[$2, $3]; }
+    | T_NEW anonymous_class
+          { list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; }
+;
+
+lexical_vars:
+      /* empty */                                           { $$ = array(); }
+    | T_USE '(' lexical_var_list ')'                        { $$ = $3; }
+;
+
+lexical_var_list:
+      lexical_var                                           { init($1); }
+    | lexical_var_list ',' lexical_var                      { push($1, $3); }
+;
+
+lexical_var:
+      optional_ref T_VARIABLE                               { $$ = Expr\ClosureUse[parseVar($2), $1]; }
+;
+
+function_call:
+      name argument_list                                    { $$ = Expr\FuncCall[$1, $2]; }
+    | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier argument_list
+          { $$ = Expr\StaticCall[$1, $3, $4]; }
+    | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
+          { $$ = Expr\StaticCall[$1, $4, $6]; }
+    | static_property argument_list {
+            if ($1 instanceof Node\Expr\StaticPropertyFetch) {
+                $$ = Expr\StaticCall[$1->class, Expr\Variable[$1->name], $2];
+            } elseif ($1 instanceof Node\Expr\ArrayDimFetch) {
+                $tmp = $1;
+                while ($tmp->var instanceof Node\Expr\ArrayDimFetch) {
+                    $tmp = $tmp->var;
+                }
+
+                $$ = Expr\StaticCall[$tmp->var->class, $1, $2];
+                $tmp->var = Expr\Variable[$tmp->var->name];
+            } else {
+                throw new \Exception;
+            }
+          }
+    | variable_without_objects argument_list
+          { $$ = Expr\FuncCall[$1, $2]; }
+    | function_call '[' dim_offset ']'                      { $$ = Expr\ArrayDimFetch[$1, $3]; }
+      /* alternative array syntax missing intentionally */
+;
+
+class_name:
+      T_STATIC                                              { $$ = Name[$1]; }
+    | name                                                  { $$ = $1; }
+;
+
+name:
+      namespace_name_parts                                  { $$ = Name[$1]; }
+    | T_NS_SEPARATOR namespace_name_parts                   { $$ = Name\FullyQualified[$2]; }
+    | T_NAMESPACE T_NS_SEPARATOR namespace_name_parts       { $$ = Name\Relative[$3]; }
+;
+
+class_name_reference:
+      class_name                                            { $$ = $1; }
+    | dynamic_class_name_reference                          { $$ = $1; }
+;
+
+dynamic_class_name_reference:
+      object_access_for_dcnr                                { $$ = $1; }
+    | base_variable                                         { $$ = $1; }
+;
+
+class_name_or_var:
+      class_name                                            { $$ = $1; }
+    | reference_variable                                    { $$ = $1; }
+;
+
+object_access_for_dcnr:
+      base_variable T_OBJECT_OPERATOR object_property
+          { $$ = Expr\PropertyFetch[$1, $3]; }
+    | object_access_for_dcnr T_OBJECT_OPERATOR object_property
+          { $$ = Expr\PropertyFetch[$1, $3]; }
+    | object_access_for_dcnr '[' dim_offset ']'             { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | object_access_for_dcnr '{' expr '}'                   { $$ = Expr\ArrayDimFetch[$1, $3]; }
+;
+
+exit_expr:
+      /* empty */                                           { $$ = null; }
+    | '(' ')'                                               { $$ = null; }
+    | parentheses_expr                                      { $$ = $1; }
+;
+
+backticks_expr:
+      /* empty */                                           { $$ = array(); }
+    | T_ENCAPSED_AND_WHITESPACE
+          { $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`', false)]); }
+    | encaps_list                                           { parseEncapsed($1, '`', false); $$ = $1; }
+;
+
+ctor_arguments:
+      /* empty */                                           { $$ = array(); }
+    | argument_list                                         { $$ = $1; }
+;
+
+common_scalar:
+      T_LNUMBER                                             { $$ = Scalar\LNumber[Scalar\LNumber::parse($1)]; }
+    | T_DNUMBER                                             { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
+    | T_CONSTANT_ENCAPSED_STRING                            { $$ = Scalar\String_[Scalar\String_::parse($1, false)]; }
+    | T_LINE                                                { $$ = Scalar\MagicConst\Line[]; }
+    | T_FILE                                                { $$ = Scalar\MagicConst\File[]; }
+    | T_DIR                                                 { $$ = Scalar\MagicConst\Dir[]; }
+    | T_CLASS_C                                             { $$ = Scalar\MagicConst\Class_[]; }
+    | T_TRAIT_C                                             { $$ = Scalar\MagicConst\Trait_[]; }
+    | T_METHOD_C                                            { $$ = Scalar\MagicConst\Method[]; }
+    | T_FUNC_C                                              { $$ = Scalar\MagicConst\Function_[]; }
+    | T_NS_C                                                { $$ = Scalar\MagicConst\Namespace_[]; }
+    | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
+          { $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2, false)]; }
+    | T_START_HEREDOC T_END_HEREDOC
+          { $$ = Scalar\String_['']; }
+;
+
+static_scalar:
+      common_scalar                                         { $$ = $1; }
+    | class_name T_PAAMAYIM_NEKUDOTAYIM identifier          { $$ = Expr\ClassConstFetch[$1, $3]; }
+    | name                                                  { $$ = Expr\ConstFetch[$1]; }
+    | T_ARRAY '(' static_array_pair_list ')'                { $$ = Expr\Array_[$3]; }
+    | '[' static_array_pair_list ']'                        { $$ = Expr\Array_[$2]; }
+    | static_operation                                      { $$ = $1; }
+;
+
+static_operation:
+      static_scalar T_BOOLEAN_OR static_scalar              { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; }
+    | static_scalar T_BOOLEAN_AND static_scalar             { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; }
+    | static_scalar T_LOGICAL_OR static_scalar              { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; }
+    | static_scalar T_LOGICAL_AND static_scalar             { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
+    | static_scalar T_LOGICAL_XOR static_scalar             { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
+    | static_scalar '|' static_scalar                       { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
+    | static_scalar '&' static_scalar                       { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
+    | static_scalar '^' static_scalar                       { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
+    | static_scalar '.' static_scalar                       { $$ = Expr\BinaryOp\Concat    [$1, $3]; }
+    | static_scalar '+' static_scalar                       { $$ = Expr\BinaryOp\Plus      [$1, $3]; }
+    | static_scalar '-' static_scalar                       { $$ = Expr\BinaryOp\Minus     [$1, $3]; }
+    | static_scalar '*' static_scalar                       { $$ = Expr\BinaryOp\Mul       [$1, $3]; }
+    | static_scalar '/' static_scalar                       { $$ = Expr\BinaryOp\Div       [$1, $3]; }
+    | static_scalar '%' static_scalar                       { $$ = Expr\BinaryOp\Mod       [$1, $3]; }
+    | static_scalar T_SL static_scalar                      { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; }
+    | static_scalar T_SR static_scalar                      { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; }
+    | static_scalar T_POW static_scalar                     { $$ = Expr\BinaryOp\Pow       [$1, $3]; }
+    | '+' static_scalar %prec T_INC                         { $$ = Expr\UnaryPlus [$2]; }
+    | '-' static_scalar %prec T_INC                         { $$ = Expr\UnaryMinus[$2]; }
+    | '!' static_scalar                                     { $$ = Expr\BooleanNot[$2]; }
+    | '~' static_scalar                                     { $$ = Expr\BitwiseNot[$2]; }
+    | static_scalar T_IS_IDENTICAL static_scalar            { $$ = Expr\BinaryOp\Identical     [$1, $3]; }
+    | static_scalar T_IS_NOT_IDENTICAL static_scalar        { $$ = Expr\BinaryOp\NotIdentical  [$1, $3]; }
+    | static_scalar T_IS_EQUAL static_scalar                { $$ = Expr\BinaryOp\Equal         [$1, $3]; }
+    | static_scalar T_IS_NOT_EQUAL static_scalar            { $$ = Expr\BinaryOp\NotEqual      [$1, $3]; }
+    | static_scalar '<' static_scalar                       { $$ = Expr\BinaryOp\Smaller       [$1, $3]; }
+    | static_scalar T_IS_SMALLER_OR_EQUAL static_scalar     { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
+    | static_scalar '>' static_scalar                       { $$ = Expr\BinaryOp\Greater       [$1, $3]; }
+    | static_scalar T_IS_GREATER_OR_EQUAL static_scalar     { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
+    | static_scalar '?' static_scalar ':' static_scalar     { $$ = Expr\Ternary[$1, $3,   $5]; }
+    | static_scalar '?' ':' static_scalar                   { $$ = Expr\Ternary[$1, null, $4]; }
+    | static_scalar '[' static_scalar ']'                   { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | '(' static_scalar ')'                                 { $$ = $2; }
+;
+
+constant:
+      name                                                  { $$ = Expr\ConstFetch[$1]; }
+    | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier
+          { $$ = Expr\ClassConstFetch[$1, $3]; }
+;
+
+scalar:
+      common_scalar                                         { $$ = $1; }
+    | constant                                              { $$ = $1; }
+    | '"' encaps_list '"'
+          { parseEncapsed($2, '"', false); $$ = Scalar\Encapsed[$2]; }
+    | T_START_HEREDOC encaps_list T_END_HEREDOC
+          { parseEncapsedDoc($2, false); $$ = Scalar\Encapsed[$2]; }
+;
+
+static_array_pair_list:
+      /* empty */                                           { $$ = array(); }
+    | non_empty_static_array_pair_list optional_comma       { $$ = $1; }
+;
+
+optional_comma:
+      /* empty */
+    | ','
+;
+
+non_empty_static_array_pair_list:
+      non_empty_static_array_pair_list ',' static_array_pair { push($1, $3); }
+    | static_array_pair                                      { init($1); }
+;
+
+static_array_pair:
+      static_scalar T_DOUBLE_ARROW static_scalar            { $$ = Expr\ArrayItem[$3, $1,   false]; }
+    | static_scalar                                         { $$ = Expr\ArrayItem[$1, null, false]; }
+;
+
+variable:
+      object_access                                         { $$ = $1; }
+    | base_variable                                         { $$ = $1; }
+    | function_call                                         { $$ = $1; }
+    | new_expr_array_deref                                  { $$ = $1; }
+;
+
+new_expr_array_deref:
+      '(' new_expr ')' '[' dim_offset ']'                   { $$ = Expr\ArrayDimFetch[$2, $5]; }
+    | new_expr_array_deref '[' dim_offset ']'               { $$ = Expr\ArrayDimFetch[$1, $3]; }
+      /* alternative array syntax missing intentionally */
+;
+
+object_access:
+      variable_or_new_expr T_OBJECT_OPERATOR object_property
+          { $$ = Expr\PropertyFetch[$1, $3]; }
+    | variable_or_new_expr T_OBJECT_OPERATOR object_property argument_list
+          { $$ = Expr\MethodCall[$1, $3, $4]; }
+    | object_access argument_list                           { $$ = Expr\FuncCall[$1, $2]; }
+    | object_access '[' dim_offset ']'                      { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | object_access '{' expr '}'                            { $$ = Expr\ArrayDimFetch[$1, $3]; }
+;
+
+variable_or_new_expr:
+      variable                                              { $$ = $1; }
+    | '(' new_expr ')'                                      { $$ = $2; }
+;
+
+variable_without_objects:
+      reference_variable                                    { $$ = $1; }
+    | '$' variable_without_objects                          { $$ = Expr\Variable[$2]; }
+;
+
+base_variable:
+      variable_without_objects                              { $$ = $1; }
+    | static_property                                       { $$ = $1; }
+;
+
+static_property:
+      class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '$' reference_variable
+          { $$ = Expr\StaticPropertyFetch[$1, $4]; }
+    | static_property_with_arrays                           { $$ = $1; }
+;
+
+static_property_with_arrays:
+      class_name_or_var T_PAAMAYIM_NEKUDOTAYIM T_VARIABLE
+          { $$ = Expr\StaticPropertyFetch[$1, parseVar($3)]; }
+    | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '$' '{' expr '}'
+          { $$ = Expr\StaticPropertyFetch[$1, $5]; }
+    | static_property_with_arrays '[' dim_offset ']'        { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | static_property_with_arrays '{' expr '}'              { $$ = Expr\ArrayDimFetch[$1, $3]; }
+;
+
+reference_variable:
+      reference_variable '[' dim_offset ']'                 { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | reference_variable '{' expr '}'                       { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
+    | '$' '{' expr '}'                                      { $$ = Expr\Variable[$3]; }
+;
+
+dim_offset:
+      /* empty */                                           { $$ = null; }
+    | expr                                                  { $$ = $1; }
+;
+
+object_property:
+      T_STRING                                              { $$ = $1; }
+    | '{' expr '}'                                          { $$ = $2; }
+    | variable_without_objects                              { $$ = $1; }
+;
+
+list_expr:
+      T_LIST '(' list_expr_elements ')'                     { $$ = Expr\List_[$3]; }
+;
+
+list_expr_elements:
+      list_expr_elements ',' list_expr_element              { push($1, $3); }
+    | list_expr_element                                     { init($1); }
+;
+
+list_expr_element:
+      variable                                              { $$ = $1; }
+    | list_expr                                             { $$ = $1; }
+    | /* empty */                                           { $$ = null; }
+;
+
+array_pair_list:
+      /* empty */                                           { $$ = array(); }
+    | non_empty_array_pair_list optional_comma              { $$ = $1; }
+;
+
+non_empty_array_pair_list:
+      non_empty_array_pair_list ',' array_pair              { push($1, $3); }
+    | array_pair                                            { init($1); }
+;
+
+array_pair:
+      expr T_DOUBLE_ARROW expr                              { $$ = Expr\ArrayItem[$3, $1,   false]; }
+    | expr                                                  { $$ = Expr\ArrayItem[$1, null, false]; }
+    | expr T_DOUBLE_ARROW '&' variable                      { $$ = Expr\ArrayItem[$4, $1,   true]; }
+    | '&' variable                                          { $$ = Expr\ArrayItem[$2, null, true]; }
+;
+
+encaps_list:
+      encaps_list encaps_var                                { push($1, $2); }
+    | encaps_list encaps_string_part                        { push($1, $2); }
+    | encaps_var                                            { init($1); }
+    | encaps_string_part encaps_var                         { init($1, $2); }
+;
+
+encaps_string_part:
+      T_ENCAPSED_AND_WHITESPACE                             { $$ = Scalar\EncapsedStringPart[$1]; }
+;
+
+encaps_var:
+      T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
+    | T_VARIABLE '[' encaps_var_offset ']'                  { $$ = Expr\ArrayDimFetch[Expr\Variable[parseVar($1)], $3]; }
+    | T_VARIABLE T_OBJECT_OPERATOR T_STRING                 { $$ = Expr\PropertyFetch[Expr\Variable[parseVar($1)], $3]; }
+    | T_DOLLAR_OPEN_CURLY_BRACES expr '}'                   { $$ = Expr\Variable[$2]; }
+    | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}'       { $$ = Expr\Variable[$2]; }
+    | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}'
+          { $$ = Expr\ArrayDimFetch[Expr\Variable[$2], $4]; }
+    | T_CURLY_OPEN variable '}'                             { $$ = $2; }
+;
+
+encaps_var_offset:
+      T_STRING                                              { $$ = Scalar\String_[$1]; }
+    | T_NUM_STRING                                          { $$ = Scalar\String_[$1]; }
+    | T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
+;
+
+%%
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/php7.y b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/php7.y
new file mode 100644
index 00000000000..dc39aabcb38
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/php7.y
@@ -0,0 +1,819 @@
+%pure_parser
+%expect 2
+
+%tokens
+
+%%
+
+start:
+    top_statement_list                                      { $$ = $this->handleNamespaces($1); }
+;
+
+top_statement_list:
+      top_statement_list top_statement                      { pushNormalizing($1, $2); }
+    | /* empty */                                           { init(); }
+;
+
+reserved_non_modifiers:
+      T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
+    | T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
+    | T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH
+    | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
+    | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
+    | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
+    | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
+;
+
+semi_reserved:
+      reserved_non_modifiers
+    | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC
+;
+
+identifier:
+      T_STRING                                              { $$ = $1; }
+    | semi_reserved                                         { $$ = $1; }
+;
+
+namespace_name_parts:
+      T_STRING                                              { init($1); }
+    | namespace_name_parts T_NS_SEPARATOR T_STRING          { push($1, $3); }
+;
+
+namespace_name:
+      namespace_name_parts                                  { $$ = Name[$1]; }
+;
+
+top_statement:
+      statement                                             { $$ = $1; }
+    | function_declaration_statement                        { $$ = $1; }
+    | class_declaration_statement                           { $$ = $1; }
+    | T_HALT_COMPILER
+          { $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; }
+    | T_NAMESPACE namespace_name ';'                        { $$ = Stmt\Namespace_[$2, null]; }
+    | T_NAMESPACE namespace_name '{' top_statement_list '}' { $$ = Stmt\Namespace_[$2, $4]; }
+    | T_NAMESPACE '{' top_statement_list '}'                { $$ = Stmt\Namespace_[null,     $3]; }
+    | T_USE use_declarations ';'                            { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
+    | T_USE use_type use_declarations ';'                   { $$ = Stmt\Use_[$3, $2]; }
+    | group_use_declaration ';'                             { $$ = $1; }
+    | T_CONST constant_declaration_list ';'                 { $$ = Stmt\Const_[$2]; }
+;
+
+use_type:
+      T_FUNCTION                                            { $$ = Stmt\Use_::TYPE_FUNCTION; }
+    | T_CONST                                               { $$ = Stmt\Use_::TYPE_CONSTANT; }
+;
+
+/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */
+group_use_declaration:
+      T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$3], $6, $2]; }
+    | T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$4], $7, $2]; }
+    | T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$2], $5, Stmt\Use_::TYPE_UNKNOWN]; }
+    | T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}'
+          { $$ = Stmt\GroupUse[Name[$3], $6, Stmt\Use_::TYPE_UNKNOWN]; }
+;
+
+unprefixed_use_declarations:
+      unprefixed_use_declarations ',' unprefixed_use_declaration
+          { push($1, $3); }
+    | unprefixed_use_declaration                            { init($1); }
+;
+
+use_declarations:
+      use_declarations ',' use_declaration                  { push($1, $3); }
+    | use_declaration                                       { init($1); }
+;
+
+inline_use_declarations:
+      inline_use_declarations ',' inline_use_declaration    { push($1, $3); }
+    | inline_use_declaration                                { init($1); }
+;
+
+unprefixed_use_declaration:
+      namespace_name                                        { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; }
+    | namespace_name T_AS T_STRING                          { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; }
+;
+
+use_declaration:
+      unprefixed_use_declaration                            { $$ = $1; }
+    | T_NS_SEPARATOR unprefixed_use_declaration             { $$ = $2; }
+;
+
+inline_use_declaration:
+      unprefixed_use_declaration                            { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; }
+    | use_type unprefixed_use_declaration                   { $$ = $2; $$->type = $1; }
+;
+
+constant_declaration_list:
+      constant_declaration_list ',' constant_declaration    { push($1, $3); }
+    | constant_declaration                                  { init($1); }
+;
+
+constant_declaration:
+    T_STRING '=' expr                                       { $$ = Node\Const_[$1, $3]; }
+;
+
+class_const_list:
+      class_const_list ',' class_const                      { push($1, $3); }
+    | class_const                                           { init($1); }
+;
+
+class_const:
+    identifier '=' expr                                     { $$ = Node\Const_[$1, $3]; }
+;
+
+inner_statement_list:
+      inner_statement_list inner_statement                  { pushNormalizing($1, $2); }
+    | /* empty */                                           { init(); }
+;
+
+inner_statement:
+      statement                                             { $$ = $1; }
+    | function_declaration_statement                        { $$ = $1; }
+    | class_declaration_statement                           { $$ = $1; }
+    | T_HALT_COMPILER
+          { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
+;
+
+non_empty_statement:
+      '{' inner_statement_list '}'                          { $$ = $2; }
+    | T_IF '(' expr ')' statement elseif_list else_single
+          { $$ = Stmt\If_[$3, ['stmts' => toArray($5), 'elseifs' => $6, 'else' => $7]]; }
+    | T_IF '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
+          { $$ = Stmt\If_[$3, ['stmts' => $6, 'elseifs' => $7, 'else' => $8]]; }
+    | T_WHILE '(' expr ')' while_statement                  { $$ = Stmt\While_[$3, $5]; }
+    | T_DO statement T_WHILE '(' expr ')' ';'               { $$ = Stmt\Do_   [$5, toArray($2)]; }
+    | T_FOR '(' for_expr ';'  for_expr ';' for_expr ')' for_statement
+          { $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; }
+    | T_SWITCH '(' expr ')' switch_case_list                { $$ = Stmt\Switch_[$3, $5]; }
+    | T_BREAK optional_expr ';'                             { $$ = Stmt\Break_[$2]; }
+    | T_CONTINUE optional_expr ';'                          { $$ = Stmt\Continue_[$2]; }
+    | T_RETURN optional_expr ';'                            { $$ = Stmt\Return_[$2]; }
+    | T_GLOBAL global_var_list ';'                          { $$ = Stmt\Global_[$2]; }
+    | T_STATIC static_var_list ';'                          { $$ = Stmt\Static_[$2]; }
+    | T_ECHO expr_list ';'                                  { $$ = Stmt\Echo_[$2]; }
+    | T_INLINE_HTML                                         { $$ = Stmt\InlineHTML[$1]; }
+    | expr ';'                                              { $$ = $1; }
+    | T_UNSET '(' variables_list ')' ';'                    { $$ = Stmt\Unset_[$3]; }
+    | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
+          { $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; }
+    | T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement
+          { $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
+    | T_DECLARE '(' declare_list ')' declare_statement      { $$ = Stmt\Declare_[$3, $5]; }
+    | T_TRY '{' inner_statement_list '}' catches optional_finally
+          { $$ = Stmt\TryCatch[$3, $5, $6]; }
+    | T_THROW expr ';'                                      { $$ = Stmt\Throw_[$2]; }
+    | T_GOTO T_STRING ';'                                   { $$ = Stmt\Goto_[$2]; }
+    | T_STRING ':'                                          { $$ = Stmt\Label[$1]; }
+    | error                                                 { $$ = array(); /* means: no statement */ }
+;
+
+statement:
+      non_empty_statement                                   { $$ = $1; }
+    | ';'                                                   { $$ = array(); /* means: no statement */ }
+;
+
+catches:
+      /* empty */                                           { init(); }
+    | catches catch                                         { push($1, $2); }
+;
+
+catch:
+    T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}'
+        { $$ = Stmt\Catch_[$3, parseVar($4), $7]; }
+;
+
+optional_finally:
+      /* empty */                                           { $$ = null; }
+    | T_FINALLY '{' inner_statement_list '}'                { $$ = $3; }
+;
+
+variables_list:
+      variable                                              { init($1); }
+    | variables_list ',' variable                           { push($1, $3); }
+;
+
+optional_ref:
+      /* empty */                                           { $$ = false; }
+    | '&'                                                   { $$ = true; }
+;
+
+optional_ellipsis:
+      /* empty */                                           { $$ = false; }
+    | T_ELLIPSIS                                            { $$ = true; }
+;
+
+function_declaration_statement:
+    T_FUNCTION optional_ref T_STRING '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
+        { $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
+;
+
+class_declaration_statement:
+      class_entry_type T_STRING extends_from implements_list '{' class_statement_list '}'
+          { $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; }
+    | T_INTERFACE T_STRING interface_extends_list '{' class_statement_list '}'
+          { $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; }
+    | T_TRAIT T_STRING '{' class_statement_list '}'
+          { $$ = Stmt\Trait_[$2, $4]; }
+;
+
+class_entry_type:
+      T_CLASS                                               { $$ = 0; }
+    | T_ABSTRACT T_CLASS                                    { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
+    | T_FINAL T_CLASS                                       { $$ = Stmt\Class_::MODIFIER_FINAL; }
+;
+
+extends_from:
+      /* empty */                                           { $$ = null; }
+    | T_EXTENDS name                                        { $$ = $2; }
+;
+
+interface_extends_list:
+      /* empty */                                           { $$ = array(); }
+    | T_EXTENDS name_list                                   { $$ = $2; }
+;
+
+implements_list:
+      /* empty */                                           { $$ = array(); }
+    | T_IMPLEMENTS name_list                                { $$ = $2; }
+;
+
+name_list:
+      name                                                  { init($1); }
+    | name_list ',' name                                    { push($1, $3); }
+;
+
+for_statement:
+      statement                                             { $$ = toArray($1); }
+    | ':' inner_statement_list T_ENDFOR ';'                 { $$ = $2; }
+;
+
+foreach_statement:
+      statement                                             { $$ = toArray($1); }
+    | ':' inner_statement_list T_ENDFOREACH ';'             { $$ = $2; }
+;
+
+declare_statement:
+      non_empty_statement                                   { $$ = toArray($1); }
+    | ';'                                                   { $$ = null; }
+    | ':' inner_statement_list T_ENDDECLARE ';'             { $$ = $2; }
+;
+
+declare_list:
+      declare_list_element                                  { init($1); }
+    | declare_list ',' declare_list_element                 { push($1, $3); }
+;
+
+declare_list_element:
+      T_STRING '=' expr                                     { $$ = Stmt\DeclareDeclare[$1, $3]; }
+;
+
+switch_case_list:
+      '{' case_list '}'                                     { $$ = $2; }
+    | '{' ';' case_list '}'                                 { $$ = $3; }
+    | ':' case_list T_ENDSWITCH ';'                         { $$ = $2; }
+    | ':' ';' case_list T_ENDSWITCH ';'                     { $$ = $3; }
+;
+
+case_list:
+      /* empty */                                           { init(); }
+    | case_list case                                        { push($1, $2); }
+;
+
+case:
+      T_CASE expr case_separator inner_statement_list       { $$ = Stmt\Case_[$2, $4]; }
+    | T_DEFAULT case_separator inner_statement_list         { $$ = Stmt\Case_[null, $3]; }
+;
+
+case_separator:
+      ':'
+    | ';'
+;
+
+while_statement:
+      statement                                             { $$ = toArray($1); }
+    | ':' inner_statement_list T_ENDWHILE ';'               { $$ = $2; }
+;
+
+elseif_list:
+      /* empty */                                           { init(); }
+    | elseif_list elseif                                    { push($1, $2); }
+;
+
+elseif:
+      T_ELSEIF '(' expr ')' statement                       { $$ = Stmt\ElseIf_[$3, toArray($5)]; }
+;
+
+new_elseif_list:
+      /* empty */                                           { init(); }
+    | new_elseif_list new_elseif                            { push($1, $2); }
+;
+
+new_elseif:
+     T_ELSEIF '(' expr ')' ':' inner_statement_list         { $$ = Stmt\ElseIf_[$3, $6]; }
+;
+
+else_single:
+      /* empty */                                           { $$ = null; }
+    | T_ELSE statement                                      { $$ = Stmt\Else_[toArray($2)]; }
+;
+
+new_else_single:
+      /* empty */                                           { $$ = null; }
+    | T_ELSE ':' inner_statement_list                       { $$ = Stmt\Else_[$3]; }
+;
+
+foreach_variable:
+      variable                                              { $$ = array($1, false); }
+    | '&' variable                                          { $$ = array($2, true); }
+    | list_expr                                             { $$ = array($1, false); }
+;
+
+parameter_list:
+      non_empty_parameter_list                              { $$ = $1; }
+    | /* empty */                                           { $$ = array(); }
+;
+
+non_empty_parameter_list:
+      parameter                                             { init($1); }
+    | non_empty_parameter_list ',' parameter                { push($1, $3); }
+;
+
+parameter:
+      optional_param_type optional_ref optional_ellipsis T_VARIABLE
+          { $$ = Node\Param[parseVar($4), null, $1, $2, $3]; }
+    | optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' expr
+          { $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; }
+;
+
+type:
+      name                                                  { $$ = $this->handleScalarTypes($1); }
+    | T_ARRAY                                               { $$ = 'array'; }
+    | T_CALLABLE                                            { $$ = 'callable'; }
+;
+
+optional_param_type:
+      /* empty */                                           { $$ = null; }
+    | type                                                  { $$ = $1; }
+;
+
+optional_return_type:
+      /* empty */                                           { $$ = null; }
+    | ':' type                                              { $$ = $2; }
+;
+
+argument_list:
+      '(' ')'                                               { $$ = array(); }
+    | '(' non_empty_argument_list ')'                       { $$ = $2; }
+;
+
+non_empty_argument_list:
+      argument                                              { init($1); }
+    | non_empty_argument_list ',' argument                  { push($1, $3); }
+;
+
+argument:
+      expr                                                  { $$ = Node\Arg[$1, false, false]; }
+    | '&' variable                                          { $$ = Node\Arg[$2, true, false]; }
+    | T_ELLIPSIS expr                                       { $$ = Node\Arg[$2, false, true]; }
+;
+
+global_var_list:
+      global_var_list ',' global_var                        { push($1, $3); }
+    | global_var                                            { init($1); }
+;
+
+global_var:
+      simple_variable                                       { $$ = Expr\Variable[$1]; }
+;
+
+static_var_list:
+      static_var_list ',' static_var                        { push($1, $3); }
+    | static_var                                            { init($1); }
+;
+
+static_var:
+      T_VARIABLE                                            { $$ = Stmt\StaticVar[parseVar($1), null]; }
+    | T_VARIABLE '=' expr                                   { $$ = Stmt\StaticVar[parseVar($1), $3]; }
+;
+
+class_statement_list:
+      class_statement_list class_statement                  { push($1, $2); }
+    | /* empty */                                           { init(); }
+;
+
+class_statement:
+      variable_modifiers property_declaration_list ';'      { $$ = Stmt\Property[$1, $2]; }
+    | T_CONST class_const_list ';'                          { $$ = Stmt\ClassConst[$2]; }
+    | method_modifiers T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type method_body
+          { $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; }
+    | T_USE name_list trait_adaptations                     { $$ = Stmt\TraitUse[$2, $3]; }
+;
+
+trait_adaptations:
+      ';'                                                   { $$ = array(); }
+    | '{' trait_adaptation_list '}'                         { $$ = $2; }
+;
+
+trait_adaptation_list:
+      /* empty */                                           { init(); }
+    | trait_adaptation_list trait_adaptation                { push($1, $2); }
+;
+
+trait_adaptation:
+      trait_method_reference_fully_qualified T_INSTEADOF name_list ';'
+          { $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
+    | trait_method_reference T_AS member_modifier identifier ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
+    | trait_method_reference T_AS member_modifier ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
+    | trait_method_reference T_AS T_STRING ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
+    | trait_method_reference T_AS reserved_non_modifiers ';'
+          { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
+;
+
+trait_method_reference_fully_qualified:
+      name T_PAAMAYIM_NEKUDOTAYIM identifier                { $$ = array($1, $3); }
+;
+trait_method_reference:
+      trait_method_reference_fully_qualified                { $$ = $1; }
+    | identifier                                            { $$ = array(null, $1); }
+;
+
+method_body:
+      ';' /* abstract method */                             { $$ = null; }
+    | '{' inner_statement_list '}'                          { $$ = $2; }
+;
+
+variable_modifiers:
+      non_empty_member_modifiers                            { $$ = $1; }
+    | T_VAR                                                 { $$ = 0; }
+;
+
+method_modifiers:
+      /* empty */                                           { $$ = 0; }
+    | non_empty_member_modifiers                            { $$ = $1; }
+;
+
+non_empty_member_modifiers:
+      member_modifier                                       { $$ = $1; }
+    | non_empty_member_modifiers member_modifier            { Stmt\Class_::verifyModifier($1, $2); $$ = $1 | $2; }
+;
+
+member_modifier:
+      T_PUBLIC                                              { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
+    | T_PROTECTED                                           { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
+    | T_PRIVATE                                             { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
+    | T_STATIC                                              { $$ = Stmt\Class_::MODIFIER_STATIC; }
+    | T_ABSTRACT                                            { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
+    | T_FINAL                                               { $$ = Stmt\Class_::MODIFIER_FINAL; }
+;
+
+property_declaration_list:
+      property_declaration                                  { init($1); }
+    | property_declaration_list ',' property_declaration    { push($1, $3); }
+;
+
+property_declaration:
+      T_VARIABLE                                            { $$ = Stmt\PropertyProperty[parseVar($1), null]; }
+    | T_VARIABLE '=' expr                                   { $$ = Stmt\PropertyProperty[parseVar($1), $3]; }
+;
+
+expr_list:
+      expr_list ',' expr                                    { push($1, $3); }
+    | expr                                                  { init($1); }
+;
+
+for_expr:
+      /* empty */                                           { $$ = array(); }
+    | expr_list                                             { $$ = $1; }
+;
+
+expr:
+      variable                                              { $$ = $1; }
+    | list_expr '=' expr                                    { $$ = Expr\Assign[$1, $3]; }
+    | variable '=' expr                                     { $$ = Expr\Assign[$1, $3]; }
+    | variable '=' '&' variable                             { $$ = Expr\AssignRef[$1, $4]; }
+    | variable '=' '&' new_expr                             { $$ = Expr\AssignRef[$1, $4]; }
+    | new_expr                                              { $$ = $1; }
+    | T_CLONE expr                                          { $$ = Expr\Clone_[$2]; }
+    | variable T_PLUS_EQUAL expr                            { $$ = Expr\AssignOp\Plus      [$1, $3]; }
+    | variable T_MINUS_EQUAL expr                           { $$ = Expr\AssignOp\Minus     [$1, $3]; }
+    | variable T_MUL_EQUAL expr                             { $$ = Expr\AssignOp\Mul       [$1, $3]; }
+    | variable T_DIV_EQUAL expr                             { $$ = Expr\AssignOp\Div       [$1, $3]; }
+    | variable T_CONCAT_EQUAL expr                          { $$ = Expr\AssignOp\Concat    [$1, $3]; }
+    | variable T_MOD_EQUAL expr                             { $$ = Expr\AssignOp\Mod       [$1, $3]; }
+    | variable T_AND_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseAnd[$1, $3]; }
+    | variable T_OR_EQUAL expr                              { $$ = Expr\AssignOp\BitwiseOr [$1, $3]; }
+    | variable T_XOR_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseXor[$1, $3]; }
+    | variable T_SL_EQUAL expr                              { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
+    | variable T_SR_EQUAL expr                              { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
+    | variable T_POW_EQUAL expr                             { $$ = Expr\AssignOp\Pow       [$1, $3]; }
+    | variable T_INC                                        { $$ = Expr\PostInc[$1]; }
+    | T_INC variable                                        { $$ = Expr\PreInc [$2]; }
+    | variable T_DEC                                        { $$ = Expr\PostDec[$1]; }
+    | T_DEC variable                                        { $$ = Expr\PreDec [$2]; }
+    | expr T_BOOLEAN_OR expr                                { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; }
+    | expr T_BOOLEAN_AND expr                               { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; }
+    | expr T_LOGICAL_OR expr                                { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; }
+    | expr T_LOGICAL_AND expr                               { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
+    | expr T_LOGICAL_XOR expr                               { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
+    | expr '|' expr                                         { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
+    | expr '&' expr                                         { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
+    | expr '^' expr                                         { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
+    | expr '.' expr                                         { $$ = Expr\BinaryOp\Concat    [$1, $3]; }
+    | expr '+' expr                                         { $$ = Expr\BinaryOp\Plus      [$1, $3]; }
+    | expr '-' expr                                         { $$ = Expr\BinaryOp\Minus     [$1, $3]; }
+    | expr '*' expr                                         { $$ = Expr\BinaryOp\Mul       [$1, $3]; }
+    | expr '/' expr                                         { $$ = Expr\BinaryOp\Div       [$1, $3]; }
+    | expr '%' expr                                         { $$ = Expr\BinaryOp\Mod       [$1, $3]; }
+    | expr T_SL expr                                        { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; }
+    | expr T_SR expr                                        { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; }
+    | expr T_POW expr                                       { $$ = Expr\BinaryOp\Pow       [$1, $3]; }
+    | '+' expr %prec T_INC                                  { $$ = Expr\UnaryPlus [$2]; }
+    | '-' expr %prec T_INC                                  { $$ = Expr\UnaryMinus[$2]; }
+    | '!' expr                                              { $$ = Expr\BooleanNot[$2]; }
+    | '~' expr                                              { $$ = Expr\BitwiseNot[$2]; }
+    | expr T_IS_IDENTICAL expr                              { $$ = Expr\BinaryOp\Identical     [$1, $3]; }
+    | expr T_IS_NOT_IDENTICAL expr                          { $$ = Expr\BinaryOp\NotIdentical  [$1, $3]; }
+    | expr T_IS_EQUAL expr                                  { $$ = Expr\BinaryOp\Equal         [$1, $3]; }
+    | expr T_IS_NOT_EQUAL expr                              { $$ = Expr\BinaryOp\NotEqual      [$1, $3]; }
+    | expr T_SPACESHIP expr                                 { $$ = Expr\BinaryOp\Spaceship     [$1, $3]; }
+    | expr '<' expr                                         { $$ = Expr\BinaryOp\Smaller       [$1, $3]; }
+    | expr T_IS_SMALLER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
+    | expr '>' expr                                         { $$ = Expr\BinaryOp\Greater       [$1, $3]; }
+    | expr T_IS_GREATER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
+    | expr T_INSTANCEOF class_name_reference                { $$ = Expr\Instanceof_[$1, $3]; }
+    | '(' expr ')'                                          { $$ = $2; }
+    | expr '?' expr ':' expr                                { $$ = Expr\Ternary[$1, $3,   $5]; }
+    | expr '?' ':' expr                                     { $$ = Expr\Ternary[$1, null, $4]; }
+    | expr T_COALESCE expr                                  { $$ = Expr\BinaryOp\Coalesce[$1, $3]; }
+    | T_ISSET '(' variables_list ')'                        { $$ = Expr\Isset_[$3]; }
+    | T_EMPTY '(' expr ')'                                  { $$ = Expr\Empty_[$3]; }
+    | T_INCLUDE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; }
+    | T_INCLUDE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; }
+    | T_EVAL '(' expr ')'                                   { $$ = Expr\Eval_[$3]; }
+    | T_REQUIRE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; }
+    | T_REQUIRE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; }
+    | T_INT_CAST expr                                       { $$ = Expr\Cast\Int_    [$2]; }
+    | T_DOUBLE_CAST expr                                    { $$ = Expr\Cast\Double  [$2]; }
+    | T_STRING_CAST expr                                    { $$ = Expr\Cast\String_ [$2]; }
+    | T_ARRAY_CAST expr                                     { $$ = Expr\Cast\Array_  [$2]; }
+    | T_OBJECT_CAST expr                                    { $$ = Expr\Cast\Object_ [$2]; }
+    | T_BOOL_CAST expr                                      { $$ = Expr\Cast\Bool_   [$2]; }
+    | T_UNSET_CAST expr                                     { $$ = Expr\Cast\Unset_  [$2]; }
+    | T_EXIT exit_expr                                      { $$ = Expr\Exit_        [$2]; }
+    | '@' expr                                              { $$ = Expr\ErrorSuppress[$2]; }
+    | scalar                                                { $$ = $1; }
+    | '`' backticks_expr '`'                                { $$ = Expr\ShellExec[$2]; }
+    | T_PRINT expr                                          { $$ = Expr\Print_[$2]; }
+    | T_YIELD                                               { $$ = Expr\Yield_[null, null]; }
+    | T_YIELD expr                                          { $$ = Expr\Yield_[$2, null]; }
+    | T_YIELD expr T_DOUBLE_ARROW expr                      { $$ = Expr\Yield_[$4, $2]; }
+    | T_YIELD_FROM expr                                     { $$ = Expr\YieldFrom[$2]; }
+    | T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
+      '{' inner_statement_list '}'
+          { $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $9]]; }
+    | T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
+      '{' inner_statement_list '}'
+          { $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $10]]; }
+;
+
+anonymous_class:
+      T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
+          { $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); }
+
+new_expr:
+      T_NEW class_name_reference ctor_arguments             { $$ = Expr\New_[$2, $3]; }
+    | T_NEW anonymous_class
+          { list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; }
+;
+
+lexical_vars:
+      /* empty */                                           { $$ = array(); }
+    | T_USE '(' lexical_var_list ')'                        { $$ = $3; }
+;
+
+lexical_var_list:
+      lexical_var                                           { init($1); }
+    | lexical_var_list ',' lexical_var                      { push($1, $3); }
+;
+
+lexical_var:
+      optional_ref T_VARIABLE                               { $$ = Expr\ClosureUse[parseVar($2), $1]; }
+;
+
+function_call:
+      name argument_list                                    { $$ = Expr\FuncCall[$1, $2]; }
+    | callable_expr argument_list                           { $$ = Expr\FuncCall[$1, $2]; }
+    | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
+          { $$ = Expr\StaticCall[$1, $3, $4]; }
+;
+
+class_name:
+      T_STATIC                                              { $$ = Name[$1]; }
+    | name                                                  { $$ = $1; }
+;
+
+name:
+      namespace_name_parts                                  { $$ = Name[$1]; }
+    | T_NS_SEPARATOR namespace_name_parts                   { $$ = Name\FullyQualified[$2]; }
+    | T_NAMESPACE T_NS_SEPARATOR namespace_name_parts       { $$ = Name\Relative[$3]; }
+;
+
+class_name_reference:
+      class_name                                            { $$ = $1; }
+    | new_variable                                          { $$ = $1; }
+;
+
+class_name_or_var:
+      class_name                                            { $$ = $1; }
+    | dereferencable                                        { $$ = $1; }
+;
+
+exit_expr:
+      /* empty */                                           { $$ = null; }
+    | '(' optional_expr ')'                                 { $$ = $2; }
+;
+
+backticks_expr:
+      /* empty */                                           { $$ = array(); }
+    | T_ENCAPSED_AND_WHITESPACE
+          { $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`')]); }
+    | encaps_list                                           { parseEncapsed($1, '`', true); $$ = $1; }
+;
+
+ctor_arguments:
+      /* empty */                                           { $$ = array(); }
+    | argument_list                                         { $$ = $1; }
+;
+
+constant:
+      name                                                  { $$ = Expr\ConstFetch[$1]; }
+    | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier
+          { $$ = Expr\ClassConstFetch[$1, $3]; }
+;
+
+dereferencable_scalar:
+      T_ARRAY '(' array_pair_list ')'                       { $$ = Expr\Array_[$3]; }
+    | '[' array_pair_list ']'                               { $$ = Expr\Array_[$2]; }
+    | T_CONSTANT_ENCAPSED_STRING                            { $$ = Scalar\String_[Scalar\String_::parse($1)]; }
+;
+
+scalar:
+      T_LNUMBER                                             { $$ = Scalar\LNumber[Scalar\LNumber::parse($1)]; }
+    | T_DNUMBER                                             { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
+    | T_LINE                                                { $$ = Scalar\MagicConst\Line[]; }
+    | T_FILE                                                { $$ = Scalar\MagicConst\File[]; }
+    | T_DIR                                                 { $$ = Scalar\MagicConst\Dir[]; }
+    | T_CLASS_C                                             { $$ = Scalar\MagicConst\Class_[]; }
+    | T_TRAIT_C                                             { $$ = Scalar\MagicConst\Trait_[]; }
+    | T_METHOD_C                                            { $$ = Scalar\MagicConst\Method[]; }
+    | T_FUNC_C                                              { $$ = Scalar\MagicConst\Function_[]; }
+    | T_NS_C                                                { $$ = Scalar\MagicConst\Namespace_[]; }
+    | dereferencable_scalar                                 { $$ = $1; }
+    | constant                                              { $$ = $1; }
+    | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
+          { $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2)]; }
+    | T_START_HEREDOC T_END_HEREDOC
+          { $$ = Scalar\String_['']; }
+    | '"' encaps_list '"'
+          { parseEncapsed($2, '"', true); $$ = Scalar\Encapsed[$2]; }
+    | T_START_HEREDOC encaps_list T_END_HEREDOC
+          { parseEncapsedDoc($2, true); $$ = Scalar\Encapsed[$2]; }
+;
+
+optional_comma:
+      /* empty */
+    | ','
+;
+
+optional_expr:
+      /* empty */                                           { $$ = null; }
+    | expr                                                  { $$ = $1; }
+;
+
+dereferencable:
+      variable                                              { $$ = $1; }
+    | '(' expr ')'                                          { $$ = $2; }
+    | dereferencable_scalar                                 { $$ = $1; }
+;
+
+callable_expr:
+      callable_variable                                     { $$ = $1; }
+    | '(' expr ')'                                          { $$ = $2; }
+    | dereferencable_scalar                                 { $$ = $1; }
+;
+
+callable_variable:
+      simple_variable                                       { $$ = Expr\Variable[$1]; }
+    | dereferencable '[' optional_expr ']'                  { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | constant '[' optional_expr ']'                        { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | dereferencable '{' expr '}'                           { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | function_call                                         { $$ = $1; }
+    | dereferencable T_OBJECT_OPERATOR property_name argument_list
+          { $$ = Expr\MethodCall[$1, $3, $4]; }
+;
+
+variable:
+      callable_variable                                     { $$ = $1; }
+    | static_member                                         { $$ = $1; }
+    | dereferencable T_OBJECT_OPERATOR property_name        { $$ = Expr\PropertyFetch[$1, $3]; }
+;
+
+simple_variable:
+      T_VARIABLE                                            { $$ = parseVar($1); }
+    | '$' '{' expr '}'                                      { $$ = $3; }
+    | '$' simple_variable                                   { $$ = Expr\Variable[$2]; }
+;
+
+static_member:
+      class_name_or_var T_PAAMAYIM_NEKUDOTAYIM simple_variable
+          { $$ = Expr\StaticPropertyFetch[$1, $3]; }
+;
+
+new_variable:
+      simple_variable                                       { $$ = Expr\Variable[$1]; }
+    | new_variable '[' optional_expr ']'                    { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | new_variable '{' expr '}'                             { $$ = Expr\ArrayDimFetch[$1, $3]; }
+    | new_variable T_OBJECT_OPERATOR property_name          { $$ = Expr\PropertyFetch[$1, $3]; }
+    | class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable     { $$ = Expr\StaticPropertyFetch[$1, $3]; }
+    | new_variable T_PAAMAYIM_NEKUDOTAYIM simple_variable   { $$ = Expr\StaticPropertyFetch[$1, $3]; }
+;
+
+member_name:
+      identifier                                            { $$ = $1; }
+    | '{' expr '}'	                                        { $$ = $2; }
+    | simple_variable	                                    { $$ = Expr\Variable[$1]; }
+;
+
+property_name:
+      T_STRING                                              { $$ = $1; }
+    | '{' expr '}'	                                        { $$ = $2; }
+    | simple_variable	                                    { $$ = Expr\Variable[$1]; }
+;
+
+list_expr:
+      T_LIST '(' list_expr_elements ')'                     { $$ = Expr\List_[$3]; }
+;
+
+list_expr_elements:
+      list_expr_elements ',' list_expr_element              { push($1, $3); }
+    | list_expr_element                                     { init($1); }
+;
+
+list_expr_element:
+      variable                                              { $$ = $1; }
+    | list_expr                                             { $$ = $1; }
+    | /* empty */                                           { $$ = null; }
+;
+
+array_pair_list:
+      /* empty */                                           { $$ = array(); }
+    | non_empty_array_pair_list optional_comma              { $$ = $1; }
+;
+
+non_empty_array_pair_list:
+      non_empty_array_pair_list ',' array_pair              { push($1, $3); }
+    | array_pair                                            { init($1); }
+;
+
+array_pair:
+      expr T_DOUBLE_ARROW expr                              { $$ = Expr\ArrayItem[$3, $1,   false]; }
+    | expr                                                  { $$ = Expr\ArrayItem[$1, null, false]; }
+    | expr T_DOUBLE_ARROW '&' variable                      { $$ = Expr\ArrayItem[$4, $1,   true]; }
+    | '&' variable                                          { $$ = Expr\ArrayItem[$2, null, true]; }
+;
+
+encaps_list:
+      encaps_list encaps_var                                { push($1, $2); }
+    | encaps_list encaps_string_part                        { push($1, $2); }
+    | encaps_var                                            { init($1); }
+    | encaps_string_part encaps_var                         { init($1, $2); }
+;
+
+encaps_string_part:
+      T_ENCAPSED_AND_WHITESPACE                             { $$ = Scalar\EncapsedStringPart[$1]; }
+;
+
+encaps_var:
+      T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
+    | T_VARIABLE '[' encaps_var_offset ']'                  { $$ = Expr\ArrayDimFetch[Expr\Variable[parseVar($1)], $3]; }
+    | T_VARIABLE T_OBJECT_OPERATOR T_STRING                 { $$ = Expr\PropertyFetch[Expr\Variable[parseVar($1)], $3]; }
+    | T_DOLLAR_OPEN_CURLY_BRACES expr '}'                   { $$ = Expr\Variable[$2]; }
+    | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}'       { $$ = Expr\Variable[$2]; }
+    | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}'
+          { $$ = Expr\ArrayDimFetch[Expr\Variable[$2], $4]; }
+    | T_CURLY_OPEN variable '}'                             { $$ = $2; }
+;
+
+encaps_var_offset:
+      T_STRING                                              { $$ = Scalar\String_[$1]; }
+    | T_NUM_STRING                                          { $$ = Scalar\String_[$1]; }
+    | T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
+;
+
+%%
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/rebuildParsers.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/rebuildParsers.php
new file mode 100644
index 00000000000..d2ac36f8e13
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/rebuildParsers.php
@@ -0,0 +1,222 @@
+ 'Php5',
+    __DIR__ . '/php7.y' => 'Php7',
+];
+
+$tokensFile     = __DIR__ . '/tokens.y';
+$tokensTemplate = __DIR__ . '/tokens.template';
+$skeletonFile   = __DIR__ . '/parser.template';
+$tmpGrammarFile = __DIR__ . '/tmp_parser.phpy';
+$tmpResultFile  = __DIR__ . '/tmp_parser.php';
+$resultDir = __DIR__ . '/../lib/PhpParser/Parser';
+$tokensResultsFile = $resultDir . '/Tokens.php';
+
+// check for kmyacc.exe binary in this directory, otherwise fall back to global name
+$kmyacc = __DIR__ . '/kmyacc.exe';
+if (!file_exists($kmyacc)) {
+    $kmyacc = 'kmyacc';
+}
+
+$options = array_flip($argv);
+$optionDebug = isset($options['--debug']);
+$optionKeepTmpGrammar = isset($options['--keep-tmp-grammar']);
+
+///////////////////////////////
+/// Utility regex constants ///
+///////////////////////////////
+
+const LIB = '(?(DEFINE)
+    (?\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
+    (?"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
+    (?(?&singleQuotedString)|(?&doubleQuotedString))
+    (?/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
+    (?\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
+)';
+
+const PARAMS = '\[(?[^[\]]*+(?:\[(?¶ms)\][^[\]]*+)*+)\]';
+const ARGS   = '\((?[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
+
+///////////////////
+/// Main script ///
+///////////////////
+
+$tokens = file_get_contents($tokensFile);
+
+foreach ($grammarFileToName as $grammarFile => $name) {
+    echo "Building temporary $name grammar file.\n";
+
+    $grammarCode = file_get_contents($grammarFile);
+    $grammarCode = str_replace('%tokens', $tokens, $grammarCode);
+
+    $grammarCode = resolveNodes($grammarCode);
+    $grammarCode = resolveMacros($grammarCode);
+    $grammarCode = resolveStackAccess($grammarCode);
+
+    file_put_contents($tmpGrammarFile, $grammarCode);
+
+    $additionalArgs = $optionDebug ? '-t -v' : '';
+
+    echo "Building $name parser.\n";
+    $output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile -p $name $tmpGrammarFile 2>&1"));
+    echo "Output: \"$output\"\n";
+
+    $resultCode = file_get_contents($tmpResultFile);
+    $resultCode = removeTrailingWhitespace($resultCode);
+
+    ensureDirExists($resultDir);
+    file_put_contents("$resultDir/$name.php", $resultCode);
+    unlink($tmpResultFile);
+
+    echo "Building token definition.\n";
+    $output = trim(shell_exec("$kmyacc -l -m $tokensTemplate $tmpGrammarFile 2>&1"));
+    assert($output === '');
+    rename($tmpResultFile, $tokensResultsFile);
+
+    if (!$optionKeepTmpGrammar) {
+        unlink($tmpGrammarFile);
+    }
+}
+
+///////////////////////////////
+/// Preprocessing functions ///
+///////////////////////////////
+
+function resolveNodes($code) {
+    return preg_replace_callback(
+        '~(?[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
+        function($matches) {
+            // recurse
+            $matches['params'] = resolveNodes($matches['params']);
+
+            $params = magicSplit(
+                '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
+                $matches['params']
+            );
+
+            $paramCode = '';
+            foreach ($params as $param) {
+                $paramCode .= $param . ', ';
+            }
+
+            return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
+        },
+        $code
+    );
+}
+
+function resolveMacros($code) {
+    return preg_replace_callback(
+        '~\b(?)(?!array\()(?[a-z][A-Za-z]++)' . ARGS . '~',
+        function($matches) {
+            // recurse
+            $matches['args'] = resolveMacros($matches['args']);
+
+            $name = $matches['name'];
+            $args = magicSplit(
+                '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
+                $matches['args']
+            );
+
+            if ('attributes' == $name) {
+                assertArgs(0, $args, $name);
+                return '$this->startAttributeStack[#1] + $this->endAttributes';
+            }
+
+            if ('init' == $name) {
+                return '$$ = array(' . implode(', ', $args) . ')';
+            }
+
+            if ('push' == $name) {
+                assertArgs(2, $args, $name);
+
+                return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
+            }
+
+            if ('pushNormalizing' == $name) {
+                assertArgs(2, $args, $name);
+
+                return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
+                     . ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
+            }
+
+            if ('toArray' == $name) {
+                assertArgs(1, $args, $name);
+
+                return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
+            }
+
+            if ('parseVar' == $name) {
+                assertArgs(1, $args, $name);
+
+                return 'substr(' . $args[0] . ', 1)';
+            }
+
+            if ('parseEncapsed' == $name) {
+                assertArgs(3, $args, $name);
+
+                return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
+                     . ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
+            }
+
+            if ('parseEncapsedDoc' == $name) {
+                assertArgs(2, $args, $name);
+
+                return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
+                     . ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, ' . $args[1] . '); } }'
+                     . ' $s->value = preg_replace(\'~(\r\n|\n|\r)\z~\', \'\', $s->value);'
+                     . ' if (\'\' === $s->value) array_pop(' . $args[0] . ');';
+            }
+
+            return $matches[0];
+        },
+        $code
+    );
+}
+
+function assertArgs($num, $args, $name) {
+    if ($num != count($args)) {
+        die('Wrong argument count for ' . $name . '().');
+    }
+}
+
+function resolveStackAccess($code) {
+    $code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
+    $code = preg_replace('/#(\d+)/', '$$1', $code);
+    return $code;
+}
+
+function removeTrailingWhitespace($code) {
+    $lines = explode("\n", $code);
+    $lines = array_map('rtrim', $lines);
+    return implode("\n", $lines);
+}
+
+function ensureDirExists($dir) {
+    if (!is_dir($dir)) {
+        mkdir($dir, 0777, true);
+    }
+}
+
+//////////////////////////////
+/// Regex helper functions ///
+//////////////////////////////
+
+function regex($regex) {
+    return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
+}
+
+function magicSplit($regex, $string) {
+    $pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
+
+    foreach ($pieces as &$piece) {
+        $piece = trim($piece);
+    }
+
+    if ($pieces === ['']) {
+        return [];
+    }
+
+    return $pieces;
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/tokens.template b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/tokens.template
new file mode 100644
index 00000000000..ba4e4901c0b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/tokens.template
@@ -0,0 +1,17 @@
+semValue
+#semval($,%t) $this->semValue
+#semval(%n) $this->stackPos-(%l-%n)
+#semval(%n,%t) $this->stackPos-(%l-%n)
+
+namespace PhpParser\Parser;
+#include;
+
+/* GENERATED file based on grammar/tokens.y */
+final class Tokens
+{
+#tokenval
+    const %s = %n;
+#endtokenval
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/tokens.y b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/tokens.y
new file mode 100644
index 00000000000..2b54f80b470
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/grammar/tokens.y
@@ -0,0 +1,113 @@
+/* We currently rely on the token ID mapping to be the same between PHP 5 and PHP 7 - so the same lexer can be used for
+ * both. This is enforced by sharing this token file. */
+
+%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
+%left ','
+%left T_LOGICAL_OR
+%left T_LOGICAL_XOR
+%left T_LOGICAL_AND
+%right T_PRINT
+%right T_YIELD
+%right T_DOUBLE_ARROW
+%right T_YIELD_FROM
+%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
+%left '?' ':'
+%right T_COALESCE
+%left T_BOOLEAN_OR
+%left T_BOOLEAN_AND
+%left '|'
+%left '^'
+%left '&'
+%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
+%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
+%left T_SL T_SR
+%left '+' '-' '.'
+%left '*' '/' '%'
+%right '!'
+%nonassoc T_INSTANCEOF
+%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
+%right T_POW
+%right '['
+%nonassoc T_NEW T_CLONE
+%token T_EXIT
+%token T_IF
+%left T_ELSEIF
+%left T_ELSE
+%left T_ENDIF
+%token T_LNUMBER
+%token T_DNUMBER
+%token T_STRING
+%token T_STRING_VARNAME
+%token T_VARIABLE
+%token T_NUM_STRING
+%token T_INLINE_HTML
+%token T_CHARACTER
+%token T_BAD_CHARACTER
+%token T_ENCAPSED_AND_WHITESPACE
+%token T_CONSTANT_ENCAPSED_STRING
+%token T_ECHO
+%token T_DO
+%token T_WHILE
+%token T_ENDWHILE
+%token T_FOR
+%token T_ENDFOR
+%token T_FOREACH
+%token T_ENDFOREACH
+%token T_DECLARE
+%token T_ENDDECLARE
+%token T_AS
+%token T_SWITCH
+%token T_ENDSWITCH
+%token T_CASE
+%token T_DEFAULT
+%token T_BREAK
+%token T_CONTINUE
+%token T_GOTO
+%token T_FUNCTION
+%token T_CONST
+%token T_RETURN
+%token T_TRY
+%token T_CATCH
+%token T_FINALLY
+%token T_THROW
+%token T_USE
+%token T_INSTEADOF
+%token T_GLOBAL
+%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
+%token T_VAR
+%token T_UNSET
+%token T_ISSET
+%token T_EMPTY
+%token T_HALT_COMPILER
+%token T_CLASS
+%token T_TRAIT
+%token T_INTERFACE
+%token T_EXTENDS
+%token T_IMPLEMENTS
+%token T_OBJECT_OPERATOR
+%token T_DOUBLE_ARROW
+%token T_LIST
+%token T_ARRAY
+%token T_CALLABLE
+%token T_CLASS_C
+%token T_TRAIT_C
+%token T_METHOD_C
+%token T_FUNC_C
+%token T_LINE
+%token T_FILE
+%token T_COMMENT
+%token T_DOC_COMMENT
+%token T_OPEN_TAG
+%token T_OPEN_TAG_WITH_ECHO
+%token T_CLOSE_TAG
+%token T_WHITESPACE
+%token T_START_HEREDOC
+%token T_END_HEREDOC
+%token T_DOLLAR_OPEN_CURLY_BRACES
+%token T_CURLY_OPEN
+%token T_PAAMAYIM_NEKUDOTAYIM
+%token T_NAMESPACE
+%token T_NS_C
+%token T_DIR
+%token T_NS_SEPARATOR
+%token T_ELLIPSIS
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Autoloader.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Autoloader.php
new file mode 100644
index 00000000000..560a8d930a4
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Autoloader.php
@@ -0,0 +1,40 @@
+name = $name;
+    }
+
+    /**
+     * Extends a class.
+     *
+     * @param Name|string $class Name of class to extend
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function extend($class) {
+        $this->extends = $this->normalizeName($class);
+
+        return $this;
+    }
+
+    /**
+     * Implements one or more interfaces.
+     *
+     * @param Name|string ...$interfaces Names of interfaces to implement
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function implement() {
+        foreach (func_get_args() as $interface) {
+            $this->implements[] = $this->normalizeName($interface);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Makes the class abstract.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeAbstract() {
+        $this->setModifier(Stmt\Class_::MODIFIER_ABSTRACT);
+
+        return $this;
+    }
+
+    /**
+     * Makes the class final.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeFinal() {
+        $this->setModifier(Stmt\Class_::MODIFIER_FINAL);
+
+        return $this;
+    }
+
+    /**
+     * Adds a statement.
+     *
+     * @param Stmt|PhpParser\Builder $stmt The statement to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addStmt($stmt) {
+        $stmt = $this->normalizeNode($stmt);
+
+        $targets = array(
+            'Stmt_TraitUse'    => &$this->uses,
+            'Stmt_ClassConst'  => &$this->constants,
+            'Stmt_Property'    => &$this->properties,
+            'Stmt_ClassMethod' => &$this->methods,
+        );
+
+        $type = $stmt->getType();
+        if (!isset($targets[$type])) {
+            throw new \LogicException(sprintf('Unexpected node of type "%s"', $type));
+        }
+
+        $targets[$type][] = $stmt;
+
+        return $this;
+    }
+
+    /**
+     * Returns the built class node.
+     *
+     * @return Stmt\Class_ The built class node
+     */
+    public function getNode() {
+        return new Stmt\Class_($this->name, array(
+            'type' => $this->type,
+            'extends' => $this->extends,
+            'implements' => $this->implements,
+            'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
+        ), $this->attributes);
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Declaration.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Declaration.php
new file mode 100644
index 00000000000..f6a322c5f06
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Declaration.php
@@ -0,0 +1,44 @@
+addStmt($stmt);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets doc comment for the declaration.
+     *
+     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function setDocComment($docComment) {
+        $this->attributes['comments'] = array(
+            $this->normalizeDocComment($docComment)
+        );
+
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/FunctionLike.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/FunctionLike.php
new file mode 100644
index 00000000000..35646a39fb2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/FunctionLike.php
@@ -0,0 +1,58 @@
+returnByRef = true;
+
+        return $this;
+    }
+
+    /**
+     * Adds a parameter.
+     *
+     * @param Node\Param|Param $param The parameter to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addParam($param) {
+        $param = $this->normalizeNode($param);
+
+        if (!$param instanceof Node\Param) {
+            throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType()));
+        }
+
+        $this->params[] = $param;
+
+        return $this;
+    }
+
+    /**
+     * Adds multiple parameters.
+     *
+     * @param array $params The parameters to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addParams(array $params) {
+        foreach ($params as $param) {
+            $this->addParam($param);
+        }
+
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Function_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Function_.php
new file mode 100644
index 00000000000..374f315de7a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Function_.php
@@ -0,0 +1,48 @@
+name = $name;
+    }
+
+    /**
+     * Adds a statement.
+     *
+     * @param Node|PhpParser\Builder $stmt The statement to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addStmt($stmt) {
+        $this->stmts[] = $this->normalizeNode($stmt);
+
+        return $this;
+    }
+
+    /**
+     * Returns the built function node.
+     *
+     * @return Stmt\Function_ The built function node
+     */
+    public function getNode() {
+        return new Stmt\Function_($this->name, array(
+            'byRef'  => $this->returnByRef,
+            'params' => $this->params,
+            'stmts'  => $this->stmts,
+        ), $this->attributes);
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Interface_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Interface_.php
new file mode 100644
index 00000000000..8ebb292a5b2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Interface_.php
@@ -0,0 +1,80 @@
+name = $name;
+    }
+
+    /**
+     * Extends one or more interfaces.
+     *
+     * @param Name|string ...$interfaces Names of interfaces to extend
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function extend() {
+        foreach (func_get_args() as $interface) {
+            $this->extends[] = $this->normalizeName($interface);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Adds a statement.
+     *
+     * @param Stmt|PhpParser\Builder $stmt The statement to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addStmt($stmt) {
+        $stmt = $this->normalizeNode($stmt);
+
+        $type = $stmt->getType();
+        switch ($type) {
+            case 'Stmt_ClassConst':
+                $this->constants[] = $stmt;
+                break;
+
+            case 'Stmt_ClassMethod':
+                // we erase all statements in the body of an interface method
+                $stmt->stmts = null;
+                $this->methods[] = $stmt;
+                break;
+
+            default:
+                throw new \LogicException(sprintf('Unexpected node of type "%s"', $type));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the built interface node.
+     *
+     * @return Stmt\Interface_ The built interface node
+     */
+    public function getNode() {
+        return new Stmt\Interface_($this->name, array(
+            'extends' => $this->extends,
+            'stmts' => array_merge($this->constants, $this->methods),
+        ), $this->attributes);
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Method.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Method.php
new file mode 100644
index 00000000000..60dd4b9a8e4
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Method.php
@@ -0,0 +1,125 @@
+name = $name;
+    }
+
+    /**
+     * Makes the method public.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makePublic() {
+        $this->setModifier(Stmt\Class_::MODIFIER_PUBLIC);
+
+        return $this;
+    }
+
+    /**
+     * Makes the method protected.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeProtected() {
+        $this->setModifier(Stmt\Class_::MODIFIER_PROTECTED);
+
+        return $this;
+    }
+
+    /**
+     * Makes the method private.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makePrivate() {
+        $this->setModifier(Stmt\Class_::MODIFIER_PRIVATE);
+
+        return $this;
+    }
+
+    /**
+     * Makes the method static.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeStatic() {
+        $this->setModifier(Stmt\Class_::MODIFIER_STATIC);
+
+        return $this;
+    }
+
+    /**
+     * Makes the method abstract.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeAbstract() {
+        if (!empty($this->stmts)) {
+            throw new \LogicException('Cannot make method with statements abstract');
+        }
+
+        $this->setModifier(Stmt\Class_::MODIFIER_ABSTRACT);
+        $this->stmts = null; // abstract methods don't have statements
+
+        return $this;
+    }
+
+    /**
+     * Makes the method final.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeFinal() {
+        $this->setModifier(Stmt\Class_::MODIFIER_FINAL);
+
+        return $this;
+    }
+
+    /**
+     * Adds a statement.
+     *
+     * @param Node|PhpParser\Builder $stmt The statement to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addStmt($stmt) {
+        if (null === $this->stmts) {
+            throw new \LogicException('Cannot add statements to an abstract method');
+        }
+
+        $this->stmts[] = $this->normalizeNode($stmt);
+
+        return $this;
+    }
+
+    /**
+     * Returns the built method node.
+     *
+     * @return Stmt\ClassMethod The built method node
+     */
+    public function getNode() {
+        return new Stmt\ClassMethod($this->name, array(
+            'type'   => $this->type,
+            'byRef'  => $this->returnByRef,
+            'params' => $this->params,
+            'stmts'  => $this->stmts,
+        ), $this->attributes);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Namespace_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Namespace_.php
new file mode 100644
index 00000000000..432fcc7f124
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Namespace_.php
@@ -0,0 +1,59 @@
+name = null !== $name ? $this->normalizeName($name) : null;
+    }
+
+    /**
+     * Adds a statement.
+     *
+     * @param Node|PhpParser\Builder $stmt The statement to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addStmt($stmt) {
+        $this->stmts[] = $this->normalizeNode($stmt);
+
+        return $this;
+    }
+
+    /**
+     * Adds multiple statements.
+     *
+     * @param array $stmts The statements to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addStmts(array $stmts) {
+        foreach ($stmts as $stmt) {
+            $this->addStmt($stmt);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the built node.
+     *
+     * @return Node The built node
+     */
+    public function getNode() {
+        return new Stmt\Namespace_($this->name, $this->stmts);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Param.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Param.php
new file mode 100644
index 00000000000..af910c209d8
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Param.php
@@ -0,0 +1,76 @@
+name = $name;
+    }
+
+    /**
+     * Sets default value for the parameter.
+     *
+     * @param mixed $value Default value to use
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function setDefault($value) {
+        $this->default = $this->normalizeValue($value);
+
+        return $this;
+    }
+
+    /**
+     * Sets type hint for the parameter.
+     *
+     * @param string|Node\Name $type Type hint to use
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function setTypeHint($type) {
+        if ($type === 'array' || $type === 'callable') {
+            $this->type = $type;
+        } else {
+            $this->type = $this->normalizeName($type);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Make the parameter accept the value by reference.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeByRef() {
+        $this->byRef = true;
+
+        return $this;
+    }
+
+    /**
+     * Returns the built parameter node.
+     *
+     * @return Node\Param The built parameter node
+     */
+    public function getNode() {
+        return new Node\Param(
+            $this->name, $this->default, $this->type, $this->byRef
+        );
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Property.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Property.php
new file mode 100644
index 00000000000..2d59d4c3089
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Property.php
@@ -0,0 +1,111 @@
+name = $name;
+    }
+
+    /**
+     * Makes the property public.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makePublic() {
+        $this->setModifier(Stmt\Class_::MODIFIER_PUBLIC);
+
+        return $this;
+    }
+
+    /**
+     * Makes the property protected.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeProtected() {
+        $this->setModifier(Stmt\Class_::MODIFIER_PROTECTED);
+
+        return $this;
+    }
+
+    /**
+     * Makes the property private.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makePrivate() {
+        $this->setModifier(Stmt\Class_::MODIFIER_PRIVATE);
+
+        return $this;
+    }
+
+    /**
+     * Makes the property static.
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function makeStatic() {
+        $this->setModifier(Stmt\Class_::MODIFIER_STATIC);
+
+        return $this;
+    }
+
+    /**
+     * Sets default value for the property.
+     *
+     * @param mixed $value Default value to use
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function setDefault($value) {
+        $this->default = $this->normalizeValue($value);
+
+        return $this;
+    }
+
+    /**
+     * Sets doc comment for the property.
+     *
+     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function setDocComment($docComment) {
+        $this->attributes = array(
+            'comments' => array($this->normalizeDocComment($docComment))
+        );
+
+        return $this;
+    }
+
+    /**
+     * Returns the built class node.
+     *
+     * @return Stmt\Property The built property node
+     */
+    public function getNode() {
+        return new Stmt\Property(
+            $this->type !== 0 ? $this->type : Stmt\Class_::MODIFIER_PUBLIC,
+            array(
+                new Stmt\PropertyProperty($this->name, $this->default)
+            ),
+            $this->attributes
+        );
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Trait_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Trait_.php
new file mode 100644
index 00000000000..9722e35734d
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Trait_.php
@@ -0,0 +1,55 @@
+name = $name;
+    }
+
+    /**
+     * Adds a statement.
+     *
+     * @param Stmt|PhpParser\Builder $stmt The statement to add
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    public function addStmt($stmt) {
+        $stmt = $this->normalizeNode($stmt);
+
+        if ($stmt instanceof Stmt\Property) {
+            $this->properties[] = $stmt;
+        } else if ($stmt instanceof Stmt\ClassMethod) {
+            $this->methods[] = $stmt;
+        } else {
+            throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the built trait node.
+     *
+     * @return Stmt\Trait_ The built interface node
+     */
+    public function getNode() {
+        return new Stmt\Trait_(
+            $this->name, array_merge($this->properties, $this->methods), $this->attributes
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Use_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Use_.php
new file mode 100644
index 00000000000..f6d3a856fad
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Builder/Use_.php
@@ -0,0 +1,58 @@
+name = $this->normalizeName($name);
+        $this->type = $type;
+    }
+
+    /**
+     * Sets alias for used name.
+     *
+     * @param string $alias Alias to use (last component of full name by default)
+     *
+     * @return $this The builder instance (for fluid interface)
+     */
+    protected function as_($alias) {
+        $this->alias = $alias;
+        return $this;
+    }
+    public function __call($name, $args) {
+        if (method_exists($this, $name . '_')) {
+            return call_user_func_array(array($this, $name . '_'), $args);
+        }
+
+        throw new \LogicException(sprintf('Method "%s" does not exist', $name));
+    }
+
+    /**
+     * Returns the built node.
+     *
+     * @return Node The built node
+     */
+    public function getNode() {
+        $alias = null !== $this->alias ? $this->alias : $this->name->getLast();
+        return new Stmt\Use_(array(
+            new Stmt\UseUse($this->name, $alias)
+        ), $this->type);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/BuilderAbstract.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/BuilderAbstract.php
new file mode 100644
index 00000000000..626bedd1dec
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/BuilderAbstract.php
@@ -0,0 +1,131 @@
+getNode();
+        } elseif ($node instanceof Node) {
+            return $node;
+        }
+
+        throw new \LogicException('Expected node or builder object');
+    }
+
+    /**
+     * Normalizes a name: Converts plain string names to PhpParser\Node\Name.
+     *
+     * @param Name|string $name The name to normalize
+     *
+     * @return Name The normalized name
+     */
+    protected function normalizeName($name) {
+        if ($name instanceof Name) {
+            return $name;
+        } elseif (is_string($name)) {
+            if (!$name) {
+                throw new \LogicException('Name cannot be empty');
+            }
+
+            if ($name[0] == '\\') {
+                return new Name\FullyQualified(substr($name, 1));
+            } elseif (0 === strpos($name, 'namespace\\')) {
+                return new Name\Relative(substr($name, strlen('namespace\\')));
+            } else {
+                return new Name($name);
+            }
+        }
+
+        throw new \LogicException('Name must be a string or an instance of PhpParser\Node\Name');
+    }
+
+    /**
+     * Normalizes a value: Converts nulls, booleans, integers,
+     * floats, strings and arrays into their respective nodes
+     *
+     * @param mixed $value The value to normalize
+     *
+     * @return Expr The normalized value
+     */
+    protected function normalizeValue($value) {
+        if ($value instanceof Node) {
+            return $value;
+        } elseif (is_null($value)) {
+            return new Expr\ConstFetch(
+                new Name('null')
+            );
+        } elseif (is_bool($value)) {
+            return new Expr\ConstFetch(
+                new Name($value ? 'true' : 'false')
+            );
+        } elseif (is_int($value)) {
+            return new Scalar\LNumber($value);
+        } elseif (is_float($value)) {
+            return new Scalar\DNumber($value);
+        } elseif (is_string($value)) {
+            return new Scalar\String_($value);
+        } elseif (is_array($value)) {
+            $items = array();
+            $lastKey = -1;
+            foreach ($value as $itemKey => $itemValue) {
+                // for consecutive, numeric keys don't generate keys
+                if (null !== $lastKey && ++$lastKey === $itemKey) {
+                    $items[] = new Expr\ArrayItem(
+                        $this->normalizeValue($itemValue)
+                    );
+                } else {
+                    $lastKey = null;
+                    $items[] = new Expr\ArrayItem(
+                        $this->normalizeValue($itemValue),
+                        $this->normalizeValue($itemKey)
+                    );
+                }
+            }
+
+            return new Expr\Array_($items);
+        } else {
+            throw new \LogicException('Invalid value');
+        }
+    }
+
+    /**
+     * Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc.
+     *
+     * @param Comment\Doc|string $docComment The doc comment to normalize
+     *
+     * @return Comment\Doc The normalized doc comment
+     */
+    protected function normalizeDocComment($docComment) {
+        if ($docComment instanceof Comment\Doc) {
+            return $docComment;
+        } else if (is_string($docComment)) {
+            return new Comment\Doc($docComment);
+        } else {
+            throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
+        }
+    }
+
+    /**
+     * Sets a modifier in the $this->type property.
+     *
+     * @param int $modifier Modifier to set
+     */
+    protected function setModifier($modifier) {
+        Stmt\Class_::verifyModifier($this->type, $modifier);
+        $this->type |= $modifier;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/BuilderFactory.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/BuilderFactory.php
new file mode 100644
index 00000000000..99c4bafed99
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/BuilderFactory.php
@@ -0,0 +1,127 @@
+text = $text;
+        $this->line = $line;
+    }
+
+    /**
+     * Gets the comment text.
+     *
+     * @return string The comment text (including comment delimiters like /*)
+     */
+    public function getText() {
+        return $this->text;
+    }
+
+    /**
+     * Sets the comment text.
+     *
+     * @param string $text The comment text (including comment delimiters like /*)
+     */
+    public function setText($text) {
+        $this->text = $text;
+    }
+
+    /**
+     * Gets the line number the comment started on.
+     *
+     * @return int Line number
+     */
+    public function getLine() {
+        return $this->line;
+    }
+
+    /**
+     * Sets the line number the comment started on.
+     *
+     * @param int $line Line number
+     */
+    public function setLine($line) {
+        $this->line = $line;
+    }
+
+    /**
+     * Gets the comment text.
+     *
+     * @return string The comment text (including comment delimiters like /*)
+     */
+    public function __toString() {
+        return $this->text;
+    }
+
+    /**
+     * Gets the reformatted comment text.
+     *
+     * "Reformatted" here means that we try to clean up the whitespace at the
+     * starts of the lines. This is necessary because we receive the comments
+     * without trailing whitespace on the first line, but with trailing whitespace
+     * on all subsequent lines.
+     *
+     * @return mixed|string
+     */
+    public function getReformattedText() {
+        $text = trim($this->text);
+        if (false === strpos($text, "\n")) {
+            // Single line comments don't need further processing
+            return $text;
+        } elseif (preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\R\s+\*.*)+$)', $text)) {
+            // Multi line comment of the type
+            //
+            //     /*
+            //      * Some text.
+            //      * Some more text.
+            //      */
+            //
+            // is handled by replacing the whitespace sequences before the * by a single space
+            return preg_replace('(^\s+\*)m', ' *', $this->text);
+        } elseif (preg_match('(^/\*\*?\s*[\r\n])', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) {
+            // Multi line comment of the type
+            //
+            //    /*
+            //        Some text.
+            //        Some more text.
+            //    */
+            //
+            // is handled by removing the whitespace sequence on the line before the closing
+            // */ on all lines. So if the last line is "    */", then "    " is removed at the
+            // start of all lines.
+            return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text);
+        } elseif (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) {
+            // Multi line comment of the type
+            //
+            //     /* Some text.
+            //        Some more text.
+            //        Even more text. */
+            //
+            // is handled by taking the length of the "/* " segment and leaving only that
+            // many space characters before the lines. Thus in the above example only three
+            // space characters are left at the start of every line.
+            return preg_replace('(^\s*(?= {' . strlen($matches[0]) . '}(?!\s)))m', '', $text);
+        }
+
+        // No idea how to format this comment, so simply return as is
+        return $text;
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Comment/Doc.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Comment/Doc.php
new file mode 100644
index 00000000000..24fc6c9ecc0
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Comment/Doc.php
@@ -0,0 +1,7 @@
+rawMessage = (string) $message;
+        if (is_array($attributes)) {
+            $this->attributes = $attributes;
+        } else {
+            $this->attributes = array('startLine' => $attributes);
+        }
+        $this->updateMessage();
+    }
+
+    /**
+     * Gets the error message
+     *
+     * @return string Error message
+     */
+    public function getRawMessage() {
+        return $this->rawMessage;
+    }
+
+    /**
+     * Gets the line the error starts in.
+     *
+     * @return int Error start line
+     */
+    public function getStartLine() {
+        return isset($this->attributes['startLine']) ? $this->attributes['startLine'] : -1;
+    }
+
+    /**
+     * Gets the line the error ends in.
+     *
+     * @return int Error end line
+     */
+    public function getEndLine() {
+        return isset($this->attributes['endLine']) ? $this->attributes['endLine'] : -1;
+    }
+
+
+    /**
+     * Gets the attributes of the node/token the error occurred at.
+     *
+     * @return array
+     */
+    public function getAttributes() {
+        return $this->attributes;
+    }
+
+    /**
+     * Sets the line of the PHP file the error occurred in.
+     *
+     * @param string $message Error message
+     */
+    public function setRawMessage($message) {
+        $this->rawMessage = (string) $message;
+        $this->updateMessage();
+    }
+
+    /**
+     * Sets the line the error starts in.
+     *
+     * @param int $line Error start line
+     */
+    public function setStartLine($line) {
+        $this->attributes['startLine'] = (int) $line;
+        $this->updateMessage();
+    }
+
+    /**
+     * Returns whether the error has start and end column information.
+     *
+     * For column information enable the startFilePos and endFilePos in the lexer options.
+     *
+     * @return bool
+     */
+    public function hasColumnInfo() {
+        return isset($this->attributes['startFilePos']) && isset($this->attributes['endFilePos']);
+    }
+
+    /**
+     * Gets the start column (1-based) into the line where the error started.
+     *
+     * @param string $code Source code of the file
+     * @return int
+     */
+    public function getStartColumn($code) {
+        if (!$this->hasColumnInfo()) {
+            throw new \RuntimeException('Error does not have column information');
+        }
+
+        return $this->toColumn($code, $this->attributes['startFilePos']);
+    }
+
+    /**
+     * Gets the end column (1-based) into the line where the error ended.
+     *
+     * @param string $code Source code of the file
+     * @return int
+     */
+    public function getEndColumn($code) {
+        if (!$this->hasColumnInfo()) {
+            throw new \RuntimeException('Error does not have column information');
+        }
+
+        return $this->toColumn($code, $this->attributes['endFilePos']);
+    }
+
+    private function toColumn($code, $pos) {
+        if ($pos > strlen($code)) {
+            throw new \RuntimeException('Invalid position information');
+        }
+
+        $lineStartPos = strrpos($code, "\n", $pos - strlen($code));
+        if (false === $lineStartPos) {
+            $lineStartPos = -1;
+        }
+
+        return $pos - $lineStartPos;
+    }
+
+    /**
+     * Updates the exception message after a change to rawMessage or rawLine.
+     */
+    protected function updateMessage() {
+        $this->message = $this->rawMessage;
+
+        if (-1 === $this->getStartLine()) {
+            $this->message .= ' on unknown line';
+        } else {
+            $this->message .= ' on line ' . $this->getStartLine();
+        }
+    }
+
+    /** @deprecated Use getStartLine() instead */
+    public function getRawLine() {
+        return $this->getStartLine();
+    }
+
+    /** @deprecated Use setStartLine() instead */
+    public function setRawLine($line) {
+        $this->setStartLine($line);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Lexer.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Lexer.php
new file mode 100644
index 00000000000..2893136976c
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Lexer.php
@@ -0,0 +1,295 @@
+tokenMap = $this->createTokenMap();
+
+        // map of tokens to drop while lexing (the map is only used for isset lookup,
+        // that's why the value is simply set to 1; the value is never actually used.)
+        $this->dropTokens = array_fill_keys(array(T_WHITESPACE, T_OPEN_TAG), 1);
+
+        // the usedAttributes member is a map of the used attribute names to a dummy
+        // value (here "true")
+        $options += array(
+            'usedAttributes' => array('comments', 'startLine', 'endLine'),
+        );
+        $this->usedAttributes = array_fill_keys($options['usedAttributes'], true);
+    }
+
+    /**
+     * Initializes the lexer for lexing the provided source code.
+     *
+     * @param string $code The source code to lex
+     *
+     * @throws Error on lexing errors (unterminated comment or unexpected character)
+     */
+    public function startLexing($code) {
+        $scream = ini_set('xdebug.scream', '0');
+
+        $this->resetErrors();
+        $this->tokens = @token_get_all($code);
+        $this->handleErrors();
+
+        if (false !== $scream) {
+            ini_set('xdebug.scream', $scream);
+        }
+
+        $this->code = $code; // keep the code around for __halt_compiler() handling
+        $this->pos  = -1;
+        $this->line =  1;
+        $this->filePos = 0;
+    }
+
+    protected function resetErrors() {
+        if (function_exists('error_clear_last')) {
+            error_clear_last();
+        } else {
+            // set error_get_last() to defined state by forcing an undefined variable error
+            set_error_handler(function() { return false; }, 0);
+            @$undefinedVariable;
+            restore_error_handler();
+        }
+    }
+
+    protected function handleErrors() {
+        $error = error_get_last();
+        if (null === $error) {
+            return;
+        }
+
+        if (preg_match(
+            '~^Unterminated comment starting line ([0-9]+)$~',
+            $error['message'], $matches
+        )) {
+            throw new Error('Unterminated comment', (int) $matches[1]);
+        }
+
+        if (preg_match(
+            '~^Unexpected character in input:  \'(.)\' \(ASCII=([0-9]+)\)~s',
+            $error['message'], $matches
+        )) {
+            throw new Error(sprintf(
+                'Unexpected character "%s" (ASCII %d)',
+                $matches[1], $matches[2]
+            ));
+        }
+
+        // PHP cuts error message after null byte, so need special case
+        if (preg_match('~^Unexpected character in input:  \'$~', $error['message'])) {
+            throw new Error('Unexpected null byte');
+        }
+    }
+
+    /**
+     * Fetches the next token.
+     *
+     * The available attributes are determined by the 'usedAttributes' option, which can
+     * be specified in the constructor. The following attributes are supported:
+     *
+     *  * 'comments'      => Array of PhpParser\Comment or PhpParser\Comment\Doc instances,
+     *                       representing all comments that occurred between the previous
+     *                       non-discarded token and the current one.
+     *  * 'startLine'     => Line in which the node starts.
+     *  * 'endLine'       => Line in which the node ends.
+     *  * 'startTokenPos' => Offset into the token array of the first token in the node.
+     *  * 'endTokenPos'   => Offset into the token array of the last token in the node.
+     *  * 'startFilePos'  => Offset into the code string of the first character that is part of the node.
+     *  * 'endFilePos'    => Offset into the code string of the last character that is part of the node
+     *
+     * @param mixed $value           Variable to store token content in
+     * @param mixed $startAttributes Variable to store start attributes in
+     * @param mixed $endAttributes   Variable to store end attributes in
+     *
+     * @return int Token id
+     */
+    public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
+        $startAttributes = array();
+        $endAttributes   = array();
+
+        while (1) {
+            if (isset($this->tokens[++$this->pos])) {
+                $token = $this->tokens[$this->pos];
+            } else {
+                // EOF token with ID 0
+                $token = "\0";
+            }
+
+            if (isset($this->usedAttributes['startTokenPos'])) {
+                $startAttributes['startTokenPos'] = $this->pos;
+            }
+            if (isset($this->usedAttributes['startFilePos'])) {
+                $startAttributes['startFilePos'] = $this->filePos;
+            }
+
+            if (is_string($token)) {
+                // bug in token_get_all
+                if ('b"' === $token) {
+                    $value = 'b"';
+                    $this->filePos += 2;
+                    $id = ord('"');
+                } else {
+                    $value = $token;
+                    $this->filePos += 1;
+                    $id = ord($token);
+                }
+
+                if (isset($this->usedAttributes['startLine'])) {
+                    $startAttributes['startLine'] = $this->line;
+                }
+                if (isset($this->usedAttributes['endLine'])) {
+                    $endAttributes['endLine'] = $this->line;
+                }
+                if (isset($this->usedAttributes['endTokenPos'])) {
+                    $endAttributes['endTokenPos'] = $this->pos;
+                }
+                if (isset($this->usedAttributes['endFilePos'])) {
+                    $endAttributes['endFilePos'] = $this->filePos - 1;
+                }
+
+                return $id;
+            } else {
+                $this->line += substr_count($token[1], "\n");
+                $this->filePos += strlen($token[1]);
+
+                if (T_COMMENT === $token[0]) {
+                    if (isset($this->usedAttributes['comments'])) {
+                        $startAttributes['comments'][] = new Comment($token[1], $token[2]);
+                    }
+                } elseif (T_DOC_COMMENT === $token[0]) {
+                    if (isset($this->usedAttributes['comments'])) {
+                        $startAttributes['comments'][] = new Comment\Doc($token[1], $token[2]);
+                    }
+                } elseif (!isset($this->dropTokens[$token[0]])) {
+                    $value = $token[1];
+
+                    if (isset($this->usedAttributes['startLine'])) {
+                        $startAttributes['startLine'] = $token[2];
+                    }
+                    if (isset($this->usedAttributes['endLine'])) {
+                        $endAttributes['endLine'] = $this->line;
+                    }
+                    if (isset($this->usedAttributes['endTokenPos'])) {
+                        $endAttributes['endTokenPos'] = $this->pos;
+                    }
+                    if (isset($this->usedAttributes['endFilePos'])) {
+                        $endAttributes['endFilePos'] = $this->filePos - 1;
+                    }
+
+                    return $this->tokenMap[$token[0]];
+                }
+            }
+        }
+
+        throw new \RuntimeException('Reached end of lexer loop');
+    }
+
+    /**
+     * Returns the token array for current code.
+     *
+     * The token array is in the same format as provided by the
+     * token_get_all() function and does not discard tokens (i.e.
+     * whitespace and comments are included). The token position
+     * attributes are against this token array.
+     *
+     * @return array Array of tokens in token_get_all() format
+     */
+    public function getTokens() {
+        return $this->tokens;
+    }
+
+    /**
+     * Handles __halt_compiler() by returning the text after it.
+     *
+     * @return string Remaining text
+     */
+    public function handleHaltCompiler() {
+        // text after T_HALT_COMPILER, still including ();
+        $textAfter = substr($this->code, $this->filePos);
+
+        // ensure that it is followed by ();
+        // this simplifies the situation, by not allowing any comments
+        // in between of the tokens.
+        if (!preg_match('~^\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) {
+            throw new Error('__HALT_COMPILER must be followed by "();"');
+        }
+
+        // prevent the lexer from returning any further tokens
+        $this->pos = count($this->tokens);
+
+        // return with (); removed
+        return (string) substr($textAfter, strlen($matches[0])); // (string) converts false to ''
+    }
+
+    /**
+     * Creates the token map.
+     *
+     * The token map maps the PHP internal token identifiers
+     * to the identifiers used by the Parser. Additionally it
+     * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'.
+     *
+     * @return array The token map
+     */
+    protected function createTokenMap() {
+        $tokenMap = array();
+
+        // 256 is the minimum possible token number, as everything below
+        // it is an ASCII value
+        for ($i = 256; $i < 1000; ++$i) {
+            if (T_DOUBLE_COLON === $i) {
+                // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
+                $tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
+            } elseif(T_OPEN_TAG_WITH_ECHO === $i) {
+                // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
+                $tokenMap[$i] = Tokens::T_ECHO;
+            } elseif(T_CLOSE_TAG === $i) {
+                // T_CLOSE_TAG is equivalent to ';'
+                $tokenMap[$i] = ord(';');
+            } elseif ('UNKNOWN' !== $name = token_name($i)) {
+                if ('T_HASHBANG' === $name) {
+                    // HHVM uses a special token for #! hashbang lines
+                    $tokenMap[$i] = Tokens::T_INLINE_HTML;
+                } else if (defined($name = 'PhpParser\Parser\Tokens::' . $name)) {
+                    // Other tokens can be mapped directly
+                    $tokenMap[$i] = constant($name);
+                }
+            }
+        }
+
+        // HHVM uses a special token for numbers that overflow to double
+        if (defined('T_ONUMBER')) {
+            $tokenMap[T_ONUMBER] = Tokens::T_DNUMBER;
+        }
+        // HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
+        if (defined('T_COMPILER_HALT_OFFSET')) {
+            $tokenMap[T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
+        }
+
+        return $tokenMap;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Lexer/Emulative.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Lexer/Emulative.php
new file mode 100644
index 00000000000..257f08681e9
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Lexer/Emulative.php
@@ -0,0 +1,205 @@
+ array(
+                'finally'       => Tokens::T_FINALLY,
+                'yield'         => Tokens::T_YIELD,
+            ),
+        );
+
+        $this->newKeywords = array();
+        foreach ($newKeywordsPerVersion as $version => $newKeywords) {
+            if (version_compare(PHP_VERSION, $version, '>=')) {
+                break;
+            }
+
+            $this->newKeywords += $newKeywords;
+        }
+
+        if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
+            return;
+        }
+        $this->tokenMap[self::T_COALESCE]   = Tokens::T_COALESCE;
+        $this->tokenMap[self::T_SPACESHIP]  = Tokens::T_SPACESHIP;
+        $this->tokenMap[self::T_YIELD_FROM] = Tokens::T_YIELD_FROM;
+
+        if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
+            return;
+        }
+        $this->tokenMap[self::T_ELLIPSIS]  = Tokens::T_ELLIPSIS;
+        $this->tokenMap[self::T_POW]       = Tokens::T_POW;
+        $this->tokenMap[self::T_POW_EQUAL] = Tokens::T_POW_EQUAL;
+    }
+
+    public function startLexing($code) {
+        $this->inObjectAccess = false;
+
+        $preprocessedCode = $this->preprocessCode($code);
+        parent::startLexing($preprocessedCode);
+        if ($preprocessedCode !== $code) {
+            $this->postprocessTokens();
+        }
+
+        // Set code property back to the original code, so __halt_compiler()
+        // handling and (start|end)FilePos attributes use the correct offsets
+        $this->code = $code;
+    }
+
+    /*
+     * Replaces new features in the code by ~__EMU__{NAME}__{DATA}__~ sequences.
+     * ~LABEL~ is never valid PHP code, that's why we can (to some degree) safely
+     * use it here.
+     * Later when preprocessing the tokens these sequences will either be replaced
+     * by real tokens or replaced with their original content (e.g. if they occurred
+     * inside a string, i.e. a place where they don't have a special meaning).
+     */
+    protected function preprocessCode($code) {
+        if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
+            return $code;
+        }
+
+        $code = str_replace('??', '~__EMU__COALESCE__~', $code);
+        $code = str_replace('<=>', '~__EMU__SPACESHIP__~', $code);
+        $code = preg_replace_callback('(yield[ \n\r\t]+from)', function($matches) {
+            // Encoding $0 in order to preserve exact whitespace
+            return '~__EMU__YIELDFROM__' . bin2hex($matches[0]) . '__~';
+        }, $code);
+
+        if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
+            return $code;
+        }
+
+        $code = str_replace('...', '~__EMU__ELLIPSIS__~', $code);
+        $code = preg_replace('((?tokens); $i < $c; ++$i) {
+            // first check that the following tokens are of form ~LABEL~,
+            // then match the __EMU__... sequence.
+            if ('~' === $this->tokens[$i]
+                && isset($this->tokens[$i + 2])
+                && '~' === $this->tokens[$i + 2]
+                && T_STRING === $this->tokens[$i + 1][0]
+                && preg_match('(^__EMU__([A-Z]++)__(?:([A-Za-z0-9]++)__)?$)', $this->tokens[$i + 1][1], $matches)
+            ) {
+                if ('ELLIPSIS' === $matches[1]) {
+                    $replace = array(
+                        array(self::T_ELLIPSIS, '...', $this->tokens[$i + 1][2])
+                    );
+                } else if ('POW' === $matches[1]) {
+                    $replace = array(
+                        array(self::T_POW, '**', $this->tokens[$i + 1][2])
+                    );
+                } else if ('POWEQUAL' === $matches[1]) {
+                    $replace = array(
+                        array(self::T_POW_EQUAL, '**=', $this->tokens[$i + 1][2])
+                    );
+                } else if ('COALESCE' === $matches[1]) {
+                    $replace = array(
+                        array(self::T_COALESCE, '??', $this->tokens[$i + 1][2])
+                    );
+                } else if ('SPACESHIP' === $matches[1]) {
+                    $replace = array(
+                        array(self::T_SPACESHIP, '<=>', $this->tokens[$i + 1][2]),
+                    );
+                } else if ('YIELDFROM' === $matches[1]) {
+                    $content = hex2bin($matches[2]);
+                    $replace = array(
+                        array(self::T_YIELD_FROM, $content, $this->tokens[$i + 1][2] - substr_count($content, "\n"))
+                    );
+                } else {
+                    throw new \RuntimeException('Invalid __EMU__ sequence');
+                }
+
+                array_splice($this->tokens, $i, 3, $replace);
+                $c -= 3 - count($replace);
+            // for multichar tokens (e.g. strings) replace any ~__EMU__...~ sequences
+            // in their content with the original character sequence
+            } elseif (is_array($this->tokens[$i])
+                      && 0 !== strpos($this->tokens[$i][1], '__EMU__')
+            ) {
+                $this->tokens[$i][1] = preg_replace_callback(
+                    '(~__EMU__([A-Z]++)__(?:([A-Za-z0-9]++)__)?~)',
+                    array($this, 'restoreContentCallback'),
+                    $this->tokens[$i][1]
+                );
+            }
+        }
+    }
+
+    /*
+     * This method is a callback for restoring EMU sequences in
+     * multichar tokens (like strings) to their original value.
+     */
+    public function restoreContentCallback(array $matches) {
+        if ('ELLIPSIS' === $matches[1]) {
+            return '...';
+        } else if ('POW' === $matches[1]) {
+            return '**';
+        } else if ('POWEQUAL' === $matches[1]) {
+            return '**=';
+        } else if ('COALESCE' === $matches[1]) {
+            return '??';
+        } else if ('SPACESHIP' === $matches[1]) {
+            return '<=>';
+        } else if ('YIELDFROM' === $matches[1]) {
+            return hex2bin($matches[2]);
+        } else {
+            return $matches[0];
+        }
+    }
+
+    public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
+        $token = parent::getNextToken($value, $startAttributes, $endAttributes);
+
+        // replace new keywords by their respective tokens. This is not done
+        // if we currently are in an object access (e.g. in $obj->namespace
+        // "namespace" stays a T_STRING tokens and isn't converted to T_NAMESPACE)
+        if (Tokens::T_STRING === $token && !$this->inObjectAccess) {
+            if (isset($this->newKeywords[strtolower($value)])) {
+                return $this->newKeywords[strtolower($value)];
+            }
+        } else {
+            // keep track of whether we currently are in an object access (after ->)
+            $this->inObjectAccess = Tokens::T_OBJECT_OPERATOR === $token;
+        }
+
+        return $token;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node.php
new file mode 100644
index 00000000000..a28ccc194bb
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node.php
@@ -0,0 +1,77 @@
+value = $value;
+        $this->byRef = $byRef;
+        $this->unpack = $unpack;
+    }
+
+    public function getSubNodeNames() {
+        return array('value', 'byRef', 'unpack');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Const_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Const_.php
new file mode 100644
index 00000000000..26322d49eb0
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Const_.php
@@ -0,0 +1,30 @@
+name = $name;
+        $this->value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('name', 'value');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr.php
new file mode 100644
index 00000000000..2dae5bfcd68
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr.php
@@ -0,0 +1,9 @@
+var = $var;
+        $this->dim = $dim;
+    }
+
+    public function getSubnodeNames() {
+        return array('var', 'dim');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ArrayItem.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ArrayItem.php
new file mode 100644
index 00000000000..de73ef467e7
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ArrayItem.php
@@ -0,0 +1,34 @@
+key = $key;
+        $this->value = $value;
+        $this->byRef = $byRef;
+    }
+
+    public function getSubNodeNames() {
+        return array('key', 'value', 'byRef');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Array_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Array_.php
new file mode 100644
index 00000000000..3ab193f9b2f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Array_.php
@@ -0,0 +1,26 @@
+items = $items;
+    }
+
+    public function getSubNodeNames() {
+        return array('items');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Assign.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Assign.php
new file mode 100644
index 00000000000..1f280ff0c74
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Assign.php
@@ -0,0 +1,30 @@
+var = $var;
+        $this->expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('var', 'expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/AssignOp.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/AssignOp.php
new file mode 100644
index 00000000000..83540a07262
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/AssignOp.php
@@ -0,0 +1,30 @@
+var = $var;
+        $this->expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('var', 'expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php
new file mode 100644
index 00000000000..9e3ed8226a3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php
@@ -0,0 +1,9 @@
+var = $var;
+        $this->expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('var', 'expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BinaryOp.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BinaryOp.php
new file mode 100644
index 00000000000..6f87e23505c
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BinaryOp.php
@@ -0,0 +1,30 @@
+left = $left;
+        $this->right = $right;
+    }
+
+    public function getSubNodeNames() {
+        return array('left', 'right');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php
new file mode 100644
index 00000000000..bd6c5c1fbd8
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php
@@ -0,0 +1,9 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BooleanNot.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BooleanNot.php
new file mode 100644
index 00000000000..8f543b822f7
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/BooleanNot.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Cast.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Cast.php
new file mode 100644
index 00000000000..bf850dc5228
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Cast.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Cast/Array_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Cast/Array_.php
new file mode 100644
index 00000000000..08b3a384c3b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Cast/Array_.php
@@ -0,0 +1,9 @@
+class = $class;
+        $this->name = $name;
+    }
+
+    public function getSubNodeNames() {
+        return array('class', 'name');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Clone_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Clone_.php
new file mode 100644
index 00000000000..198ec4d8917
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Clone_.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Closure.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Closure.php
new file mode 100644
index 00000000000..2686086ebfa
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Closure.php
@@ -0,0 +1,65 @@
+ false  : Whether the closure is static
+     *                          'byRef'      => false  : Whether to return by reference
+     *                          'params'     => array(): Parameters
+     *                          'uses'       => array(): use()s
+     *                          'returnType' => null   : Return type
+     *                          'stmts'      => array(): Statements
+     * @param array $attributes Additional attributes
+     */
+    public function __construct(array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->static = isset($subNodes['static']) ? $subNodes['static'] : false;
+        $this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
+        $this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
+        $this->uses = isset($subNodes['uses']) ? $subNodes['uses'] : array();
+        $this->returnType = isset($subNodes['returnType']) ? $subNodes['returnType'] : null;
+        $this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
+    }
+
+    public function getSubNodeNames() {
+        return array('static', 'byRef', 'params', 'uses', 'returnType', 'stmts');
+    }
+
+    public function returnsByRef() {
+        return $this->byRef;
+    }
+
+    public function getParams() {
+        return $this->params;
+    }
+
+    public function getReturnType() {
+        return $this->returnType;
+    }
+
+    public function getStmts() {
+        return $this->stmts;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ClosureUse.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ClosureUse.php
new file mode 100644
index 00000000000..f4d02023ef1
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ClosureUse.php
@@ -0,0 +1,30 @@
+var = $var;
+        $this->byRef = $byRef;
+    }
+
+    public function getSubNodeNames() {
+        return array('var', 'byRef');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ConstFetch.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ConstFetch.php
new file mode 100644
index 00000000000..5f1a92d8856
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ConstFetch.php
@@ -0,0 +1,27 @@
+name = $name;
+    }
+
+    public function getSubNodeNames() {
+        return array('name');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Empty_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Empty_.php
new file mode 100644
index 00000000000..0c0509efbbe
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Empty_.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ErrorSuppress.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ErrorSuppress.php
new file mode 100644
index 00000000000..750c814c751
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ErrorSuppress.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Eval_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Eval_.php
new file mode 100644
index 00000000000..eadffd008fa
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Eval_.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Exit_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Exit_.php
new file mode 100644
index 00000000000..dec96ffd6bf
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Exit_.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/FuncCall.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/FuncCall.php
new file mode 100644
index 00000000000..60d00507d17
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/FuncCall.php
@@ -0,0 +1,31 @@
+name = $name;
+        $this->args = $args;
+    }
+
+    public function getSubNodeNames() {
+        return array('name', 'args');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Include_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Include_.php
new file mode 100644
index 00000000000..b27f3af18f6
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Include_.php
@@ -0,0 +1,35 @@
+expr = $expr;
+        $this->type = $type;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr', 'type');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Instanceof_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Instanceof_.php
new file mode 100644
index 00000000000..3ef8ad11a66
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Instanceof_.php
@@ -0,0 +1,31 @@
+expr = $expr;
+        $this->class = $class;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr', 'class');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Isset_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Isset_.php
new file mode 100644
index 00000000000..4ec1d025a5a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Isset_.php
@@ -0,0 +1,26 @@
+vars = $vars;
+    }
+
+    public function getSubNodeNames() {
+        return array('vars');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/List_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/List_.php
new file mode 100644
index 00000000000..28ba6121ded
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/List_.php
@@ -0,0 +1,26 @@
+vars = $vars;
+    }
+
+    public function getSubNodeNames() {
+        return array('vars');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/MethodCall.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/MethodCall.php
new file mode 100644
index 00000000000..a6cdd07c1ac
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/MethodCall.php
@@ -0,0 +1,35 @@
+var = $var;
+        $this->name = $name;
+        $this->args = $args;
+    }
+
+    public function getSubNodeNames() {
+        return array('var', 'name', 'args');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/New_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/New_.php
new file mode 100644
index 00000000000..a8c87794db9
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/New_.php
@@ -0,0 +1,31 @@
+class = $class;
+        $this->args = $args;
+    }
+
+    public function getSubNodeNames() {
+        return array('class', 'args');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PostDec.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PostDec.php
new file mode 100644
index 00000000000..06ce5470eb2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PostDec.php
@@ -0,0 +1,26 @@
+var = $var;
+    }
+
+    public function getSubNodeNames() {
+        return array('var');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PostInc.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PostInc.php
new file mode 100644
index 00000000000..54865ba12c5
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PostInc.php
@@ -0,0 +1,26 @@
+var = $var;
+    }
+
+    public function getSubNodeNames() {
+        return array('var');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PreDec.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PreDec.php
new file mode 100644
index 00000000000..db30f51026f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PreDec.php
@@ -0,0 +1,26 @@
+var = $var;
+    }
+
+    public function getSubNodeNames() {
+        return array('var');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PreInc.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PreInc.php
new file mode 100644
index 00000000000..06356028ce2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PreInc.php
@@ -0,0 +1,26 @@
+var = $var;
+    }
+
+    public function getSubNodeNames() {
+        return array('var');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Print_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Print_.php
new file mode 100644
index 00000000000..0666ab84d72
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Print_.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PropertyFetch.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PropertyFetch.php
new file mode 100644
index 00000000000..adcf21ce11e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/PropertyFetch.php
@@ -0,0 +1,30 @@
+var = $var;
+        $this->name = $name;
+    }
+
+    public function getSubNodeNames() {
+        return array('var', 'name');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ShellExec.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ShellExec.php
new file mode 100644
index 00000000000..9516a32f492
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/ShellExec.php
@@ -0,0 +1,26 @@
+parts = $parts;
+    }
+
+    public function getSubNodeNames() {
+        return array('parts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/StaticCall.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/StaticCall.php
new file mode 100644
index 00000000000..5118f34a70e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/StaticCall.php
@@ -0,0 +1,35 @@
+class = $class;
+        $this->name = $name;
+        $this->args = $args;
+    }
+
+    public function getSubNodeNames() {
+        return array('class', 'name', 'args');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php
new file mode 100644
index 00000000000..5126bda5410
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php
@@ -0,0 +1,31 @@
+class = $class;
+        $this->name = $name;
+    }
+
+    public function getSubNodeNames() {
+        return array('class', 'name');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Ternary.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Ternary.php
new file mode 100644
index 00000000000..57301390fec
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Ternary.php
@@ -0,0 +1,34 @@
+cond = $cond;
+        $this->if = $if;
+        $this->else = $else;
+    }
+
+    public function getSubNodeNames() {
+        return array('cond', 'if', 'else');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/UnaryMinus.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/UnaryMinus.php
new file mode 100644
index 00000000000..94635d6383b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/UnaryMinus.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/UnaryPlus.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/UnaryPlus.php
new file mode 100644
index 00000000000..651412003bc
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/UnaryPlus.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Variable.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Variable.php
new file mode 100644
index 00000000000..3ae3ecd9b24
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Variable.php
@@ -0,0 +1,26 @@
+name = $name;
+    }
+
+    public function getSubNodeNames() {
+        return array('name');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/YieldFrom.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/YieldFrom.php
new file mode 100644
index 00000000000..993c82c20a0
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/YieldFrom.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Yield_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Yield_.php
new file mode 100644
index 00000000000..f3ca88e05bf
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Expr/Yield_.php
@@ -0,0 +1,30 @@
+key = $key;
+        $this->value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('key', 'value');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/FunctionLike.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/FunctionLike.php
new file mode 100644
index 00000000000..efe4333327c
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/FunctionLike.php
@@ -0,0 +1,36 @@
+parts = $parts;
+    }
+
+    public function getSubNodeNames() {
+        return array('parts');
+    }
+
+    /**
+     * Gets the first part of the name, i.e. everything before the first namespace separator.
+     *
+     * @return string First part of the name
+     */
+    public function getFirst() {
+        return $this->parts[0];
+    }
+
+    /**
+     * Gets the last part of the name, i.e. everything after the last namespace separator.
+     *
+     * @return string Last part of the name
+     */
+    public function getLast() {
+        return $this->parts[count($this->parts) - 1];
+    }
+
+    /**
+     * Checks whether the name is unqualified. (E.g. Name)
+     *
+     * @return bool Whether the name is unqualified
+     */
+    public function isUnqualified() {
+        return 1 == count($this->parts);
+    }
+
+    /**
+     * Checks whether the name is qualified. (E.g. Name\Name)
+     *
+     * @return bool Whether the name is qualified
+     */
+    public function isQualified() {
+        return 1 < count($this->parts);
+    }
+
+    /**
+     * Checks whether the name is fully qualified. (E.g. \Name)
+     *
+     * @return bool Whether the name is fully qualified
+     */
+    public function isFullyQualified() {
+        return false;
+    }
+
+    /**
+     * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
+     *
+     * @return bool Whether the name is relative
+     */
+    public function isRelative() {
+        return false;
+    }
+
+    /**
+     * Returns a string representation of the name by imploding the namespace parts with a separator.
+     *
+     * @param string $separator The separator to use (defaults to the namespace separator \)
+     *
+     * @return string String representation
+     */
+    public function toString($separator = '\\') {
+        return implode($separator, $this->parts);
+    }
+
+    /**
+     * Returns a string representation of the name by imploding the namespace parts with the
+     * namespace separator.
+     *
+     * @return string String representation
+     */
+    public function __toString() {
+        return implode('\\', $this->parts);
+    }
+
+    /**
+     * Sets the whole name.
+     *
+     * @deprecated Create a new Name instead, or manually modify the $parts property
+     *
+     * @param string|array|self $name The name to set the whole name to
+     */
+    public function set($name) {
+        $this->parts = self::prepareName($name);
+    }
+
+    /**
+     * Prepends a name to this name.
+     *
+     * @deprecated Use Name::concat($name1, $name2) instead
+     *
+     * @param string|array|self $name Name to prepend
+     */
+    public function prepend($name) {
+        $this->parts = array_merge(self::prepareName($name), $this->parts);
+    }
+
+    /**
+     * Appends a name to this name.
+     *
+     * @deprecated Use Name::concat($name1, $name2) instead
+     *
+     * @param string|array|self $name Name to append
+     */
+    public function append($name) {
+        $this->parts = array_merge($this->parts, self::prepareName($name));
+    }
+
+    /**
+     * Sets the first part of the name.
+     *
+     * @deprecated Use concat($first, $name->slice(1)) instead
+     *
+     * @param string|array|self $name The name to set the first part to
+     */
+    public function setFirst($name) {
+        array_splice($this->parts, 0, 1, self::prepareName($name));
+    }
+
+    /**
+     * Sets the last part of the name.
+     *
+     * @param string|array|self $name The name to set the last part to
+     */
+    public function setLast($name) {
+        array_splice($this->parts, -1, 1, self::prepareName($name));
+    }
+
+    /**
+     * Gets a slice of a name (similar to array_slice).
+     *
+     * This method returns a new instance of the same type as the original and with the same
+     * attributes.
+     *
+     * If the slice is empty, a Name with an empty parts array is returned. While this is
+     * meaningless in itself, it works correctly in conjunction with concat().
+     *
+     * @param int $offset Offset to start the slice at
+     *
+     * @return static Sliced name
+     */
+    public function slice($offset) {
+        // TODO negative offset and length
+        if ($offset < 0 || $offset > count($this->parts)) {
+            throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset));
+        }
+
+        return new static(array_slice($this->parts, $offset), $this->attributes);
+    }
+
+    /**
+     * Concatenate two names, yielding a new Name instance.
+     *
+     * The type of the generated instance depends on which class this method is called on, for
+     * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance.
+     *
+     * @param string|array|self $name1      The first name
+     * @param string|array|self $name2      The second name
+     * @param array             $attributes Attributes to assign to concatenated name
+     *
+     * @return static Concatenated name
+     */
+    public static function concat($name1, $name2, array $attributes = []) {
+        return new static(
+            array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes
+        );
+    }
+
+    /**
+     * Prepares a (string, array or Name node) name for use in name changing methods by converting
+     * it to an array.
+     *
+     * @param string|array|self $name Name to prepare
+     *
+     * @return array Prepared name
+     */
+    private static function prepareName($name) {
+        if (is_string($name)) {
+            return explode('\\', $name);
+        } elseif (is_array($name)) {
+            return $name;
+        } elseif ($name instanceof self) {
+            return $name->parts;
+        }
+
+        throw new \InvalidArgumentException(
+            'When changing a name you need to pass either a string, an array or a Name node'
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Name/FullyQualified.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Name/FullyQualified.php
new file mode 100644
index 00000000000..97cc1113d2b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Name/FullyQualified.php
@@ -0,0 +1,42 @@
+type = $type;
+        $this->byRef = $byRef;
+        $this->variadic = $variadic;
+        $this->name = $name;
+        $this->default = $default;
+
+        if ($variadic && null !== $default) {
+            throw new Error('Variadic parameter cannot have a default value', $default->getAttributes());
+        }
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'byRef', 'variadic', 'name', 'default');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar.php
new file mode 100644
index 00000000000..01177532cd8
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar.php
@@ -0,0 +1,7 @@
+value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('value');
+    }
+
+    /**
+     * @internal
+     *
+     * Parses a DNUMBER token like PHP would.
+     *
+     * @param string $str A string number
+     *
+     * @return float The parsed number
+     */
+    public static function parse($str) {
+        // if string contains any of .eE just cast it to float
+        if (false !== strpbrk($str, '.eE')) {
+            return (float) $str;
+        }
+
+        // otherwise it's an integer notation that overflowed into a float
+        // if it starts with 0 it's one of the special integer notations
+        if ('0' === $str[0]) {
+            // hex
+            if ('x' === $str[1] || 'X' === $str[1]) {
+                return hexdec($str);
+            }
+
+            // bin
+            if ('b' === $str[1] || 'B' === $str[1]) {
+                return bindec($str);
+            }
+
+            // oct
+            // substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9)
+            // so that only the digits before that are used
+            return octdec(substr($str, 0, strcspn($str, '89')));
+        }
+
+        // dec
+        return (float) $str;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/Encapsed.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/Encapsed.php
new file mode 100644
index 00000000000..ec3e9712a0b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/Encapsed.php
@@ -0,0 +1,26 @@
+parts = $parts;
+    }
+
+    public function getSubNodeNames() {
+        return array('parts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php
new file mode 100644
index 00000000000..50cb1afe6cd
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php
@@ -0,0 +1,26 @@
+value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('value');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/LNumber.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/LNumber.php
new file mode 100644
index 00000000000..3d9c90218fa
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/LNumber.php
@@ -0,0 +1,61 @@
+value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('value');
+    }
+
+    /**
+     * @internal
+     *
+     * Parses an LNUMBER token (dec, hex, oct and bin notations) like PHP would.
+     *
+     * @param string $str A string number
+     *
+     * @return int The parsed number
+     */
+    public static function parse($str) {
+        // handle plain 0 specially
+        if ('0' === $str) {
+            return 0;
+        }
+
+        // if first char is 0 (and number isn't 0) it's a special syntax
+        if ('0' === $str[0]) {
+            // hex
+            if ('x' === $str[1] || 'X' === $str[1]) {
+                return hexdec($str);
+            }
+
+            // bin
+            if ('b' === $str[1] || 'B' === $str[1]) {
+                return bindec($str);
+            }
+
+            // oct (intval instead of octdec to get proper cutting behavior with malformed numbers)
+            return intval($str, 8);
+        }
+
+        // dec
+        return (int) $str;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/MagicConst.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/MagicConst.php
new file mode 100644
index 00000000000..a50d68f3d0f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Scalar/MagicConst.php
@@ -0,0 +1,28 @@
+ '\\',
+        '$'  =>  '$',
+        'n'  => "\n",
+        'r'  => "\r",
+        't'  => "\t",
+        'f'  => "\f",
+        'v'  => "\v",
+        'e'  => "\x1B",
+    );
+
+    /**
+     * Constructs a string scalar node.
+     *
+     * @param string $value      Value of the string
+     * @param array  $attributes Additional attributes
+     */
+    public function __construct($value, array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('value');
+    }
+
+    /**
+     * @internal
+     *
+     * Parses a string token.
+     *
+     * @param string $str String token content
+     * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
+     *
+     * @return string The parsed string
+     */
+    public static function parse($str, $parseUnicodeEscape = true) {
+        $bLength = 0;
+        if ('b' === $str[0]) {
+            $bLength = 1;
+        }
+
+        if ('\'' === $str[$bLength]) {
+            return str_replace(
+                array('\\\\', '\\\''),
+                array(  '\\',   '\''),
+                substr($str, $bLength + 1, -1)
+            );
+        } else {
+            return self::parseEscapeSequences(
+                substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape
+            );
+        }
+    }
+
+    /**
+     * @internal
+     *
+     * Parses escape sequences in strings (all string types apart from single quoted).
+     *
+     * @param string      $str   String without quotes
+     * @param null|string $quote Quote type
+     * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
+     *
+     * @return string String with escape sequences parsed
+     */
+    public static function parseEscapeSequences($str, $quote, $parseUnicodeEscape = true) {
+        if (null !== $quote) {
+            $str = str_replace('\\' . $quote, $quote, $str);
+        }
+
+        $extra = '';
+        if ($parseUnicodeEscape) {
+            $extra = '|u\{([0-9a-fA-F]+)\}';
+        }
+
+        return preg_replace_callback(
+            '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~',
+            function($matches) {
+                $str = $matches[1];
+
+                if (isset(self::$replacements[$str])) {
+                    return self::$replacements[$str];
+                } elseif ('x' === $str[0] || 'X' === $str[0]) {
+                    return chr(hexdec($str));
+                } elseif ('u' === $str[0]) {
+                    return self::codePointToUtf8(hexdec($matches[2]));
+                } else {
+                    return chr(octdec($str));
+                }
+            },
+            $str
+        );
+    }
+
+    private static function codePointToUtf8($num) {
+        if ($num <= 0x7F) {
+            return chr($num);
+        }
+        if ($num <= 0x7FF) {
+            return chr(($num>>6) + 0xC0) . chr(($num&0x3F) + 0x80);
+        }
+        if ($num <= 0xFFFF) {
+            return chr(($num>>12) + 0xE0) . chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80);
+        }
+        if ($num <= 0x1FFFFF) {
+            return chr(($num>>18) + 0xF0) . chr((($num>>12)&0x3F) + 0x80)
+                 . chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80);
+        }
+        throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large');
+    }
+
+    /**
+     * @internal
+     *
+     * Parses a constant doc string.
+     *
+     * @param string $startToken Doc string start token content (<<num = $num;
+    }
+
+    public function getSubNodeNames() {
+        return array('num');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Case_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Case_.php
new file mode 100644
index 00000000000..03f892c7cb3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Case_.php
@@ -0,0 +1,30 @@
+cond = $cond;
+        $this->stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('cond', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Catch_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Catch_.php
new file mode 100644
index 00000000000..2656afdd64e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Catch_.php
@@ -0,0 +1,34 @@
+type = $type;
+        $this->var = $var;
+        $this->stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'var', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassConst.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassConst.php
new file mode 100644
index 00000000000..e8e2d6f1c5c
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassConst.php
@@ -0,0 +1,26 @@
+consts = $consts;
+    }
+
+    public function getSubNodeNames() {
+        return array('consts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassLike.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassLike.php
new file mode 100644
index 00000000000..f6831a613ed
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassLike.php
@@ -0,0 +1,44 @@
+stmts as $stmt) {
+            if ($stmt instanceof ClassMethod) {
+                $methods[] = $stmt;
+            }
+        }
+        return $methods;
+    }
+
+    /**
+     * Gets method with the given name defined directly in this class/interface/trait.
+     *
+     * @param string $name Name of the method (compared case-insensitively)
+     *
+     * @return ClassMethod|null Method node or null if the method does not exist
+     */
+    public function getMethod($name) {
+        $lowerName = strtolower($name);
+        foreach ($this->stmts as $stmt) {
+            if ($stmt instanceof ClassMethod && $lowerName === strtolower($stmt->name)) {
+                return $stmt;
+            }
+        }
+        return null;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassMethod.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassMethod.php
new file mode 100644
index 00000000000..dfdd8aba10e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ClassMethod.php
@@ -0,0 +1,101 @@
+ MODIFIER_PUBLIC: Type
+     *                                'byRef'      => false          : Whether to return by reference
+     *                                'params'     => array()        : Parameters
+     *                                'returnType' => null           : Return type
+     *                                'stmts'      => array()        : Statements
+     * @param array       $attributes Additional attributes
+     */
+    public function __construct($name, array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
+        $this->byRef = isset($subNodes['byRef'])  ? $subNodes['byRef']  : false;
+        $this->name = $name;
+        $this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
+        $this->returnType = isset($subNodes['returnType']) ? $subNodes['returnType'] : null;
+        $this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : array();
+
+        if ($this->type & Class_::MODIFIER_STATIC) {
+            switch (strtolower($this->name)) {
+                case '__construct':
+                    throw new Error(sprintf('Constructor %s() cannot be static', $this->name));
+                case '__destruct':
+                    throw new Error(sprintf('Destructor %s() cannot be static', $this->name));
+                case '__clone':
+                    throw new Error(sprintf('Clone method %s() cannot be static', $this->name));
+            }
+        }
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'byRef', 'name', 'params', 'returnType', 'stmts');
+    }
+
+    public function returnsByRef() {
+        return $this->byRef;
+    }
+
+    public function getParams() {
+        return $this->params;
+    }
+
+    public function getReturnType() {
+        return $this->returnType;
+    }
+
+    public function getStmts() {
+        return $this->stmts;
+    }
+
+    public function isPublic() {
+        return ($this->type & Class_::MODIFIER_PUBLIC) !== 0
+            || ($this->type & Class_::VISIBILITY_MODIFER_MASK) === 0;
+    }
+
+    public function isProtected() {
+        return (bool) ($this->type & Class_::MODIFIER_PROTECTED);
+    }
+
+    public function isPrivate() {
+        return (bool) ($this->type & Class_::MODIFIER_PRIVATE);
+    }
+
+    public function isAbstract() {
+        return (bool) ($this->type & Class_::MODIFIER_ABSTRACT);
+    }
+
+    public function isFinal() {
+        return (bool) ($this->type & Class_::MODIFIER_FINAL);
+    }
+
+    public function isStatic() {
+        return (bool) ($this->type & Class_::MODIFIER_STATIC);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Class_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Class_.php
new file mode 100644
index 00000000000..6ef093dcd02
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Class_.php
@@ -0,0 +1,112 @@
+ true,
+        'parent' => true,
+        'static' => true,
+    );
+
+    /**
+     * Constructs a class node.
+     *
+     * @param string|null $name       Name
+     * @param array       $subNodes   Array of the following optional subnodes:
+     *                                'type'       => 0      : Type
+     *                                'extends'    => null   : Name of extended class
+     *                                'implements' => array(): Names of implemented interfaces
+     *                                'stmts'      => array(): Statements
+     * @param array       $attributes Additional attributes
+     */
+    public function __construct($name, array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->type = isset($subNodes['type']) ? $subNodes['type'] : 0;
+        $this->name = $name;
+        $this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : null;
+        $this->implements = isset($subNodes['implements']) ? $subNodes['implements'] : array();
+        $this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
+
+        if (null !== $this->name && isset(self::$specialNames[strtolower($this->name)])) {
+            throw new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $this->name));
+        }
+
+        if (isset(self::$specialNames[strtolower($this->extends)])) {
+            throw new Error(
+                sprintf('Cannot use \'%s\' as class name as it is reserved', $this->extends),
+                $this->extends->getAttributes()
+            );
+        }
+
+        foreach ($this->implements as $interface) {
+            if (isset(self::$specialNames[strtolower($interface)])) {
+                throw new Error(
+                    sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
+                    $interface->getAttributes()
+                );
+            }
+        }
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'name', 'extends', 'implements', 'stmts');
+    }
+
+    public function isAbstract() {
+        return (bool) ($this->type & self::MODIFIER_ABSTRACT);
+    }
+
+    public function isFinal() {
+        return (bool) ($this->type & self::MODIFIER_FINAL);
+    }
+
+    public function isAnonymous() {
+        return null === $this->name;
+    }
+
+    /**
+     * @internal
+     */
+    public static function verifyModifier($a, $b) {
+        if ($a & self::VISIBILITY_MODIFER_MASK && $b & self::VISIBILITY_MODIFER_MASK) {
+            throw new Error('Multiple access type modifiers are not allowed');
+        }
+
+        if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) {
+            throw new Error('Multiple abstract modifiers are not allowed');
+        }
+
+        if ($a & self::MODIFIER_STATIC && $b & self::MODIFIER_STATIC) {
+            throw new Error('Multiple static modifiers are not allowed');
+        }
+
+        if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) {
+            throw new Error('Multiple final modifiers are not allowed');
+        }
+
+        if ($a & 48 && $b & 48) {
+            throw new Error('Cannot use the final modifier on an abstract class member');
+        }
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Const_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Const_.php
new file mode 100644
index 00000000000..8b2eecd51d7
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Const_.php
@@ -0,0 +1,26 @@
+consts = $consts;
+    }
+
+    public function getSubNodeNames() {
+        return array('consts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Continue_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Continue_.php
new file mode 100644
index 00000000000..f78e19a2aae
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Continue_.php
@@ -0,0 +1,26 @@
+num = $num;
+    }
+
+    public function getSubNodeNames() {
+        return array('num');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php
new file mode 100644
index 00000000000..829dbaf25c3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php
@@ -0,0 +1,30 @@
+value pair node.
+     *
+     * @param string    $key        Key
+     * @param Node\Expr $value      Value
+     * @param array     $attributes Additional attributes
+     */
+    public function __construct($key, Node\Expr $value, array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->key = $key;
+        $this->value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('key', 'value');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Declare_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Declare_.php
new file mode 100644
index 00000000000..64b9efc33e3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Declare_.php
@@ -0,0 +1,29 @@
+declares = $declares;
+        $this->stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('declares', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Do_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Do_.php
new file mode 100644
index 00000000000..dd4c6c84362
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Do_.php
@@ -0,0 +1,30 @@
+cond = $cond;
+        $this->stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('cond', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Echo_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Echo_.php
new file mode 100644
index 00000000000..11e1070725e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Echo_.php
@@ -0,0 +1,26 @@
+exprs = $exprs;
+    }
+
+    public function getSubNodeNames() {
+        return array('exprs');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ElseIf_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ElseIf_.php
new file mode 100644
index 00000000000..608878f6597
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/ElseIf_.php
@@ -0,0 +1,30 @@
+cond = $cond;
+        $this->stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('cond', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Else_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Else_.php
new file mode 100644
index 00000000000..c91a1484f6a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Else_.php
@@ -0,0 +1,26 @@
+stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/For_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/For_.php
new file mode 100644
index 00000000000..2ca88a332d6
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/For_.php
@@ -0,0 +1,39 @@
+ array(): Init expressions
+     *                          'cond'  => array(): Loop conditions
+     *                          'loop'  => array(): Loop expressions
+     *                          'stmts' => array(): Statements
+     * @param array $attributes Additional attributes
+     */
+    public function __construct(array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->init = isset($subNodes['init']) ? $subNodes['init'] : array();
+        $this->cond = isset($subNodes['cond']) ? $subNodes['cond'] : array();
+        $this->loop = isset($subNodes['loop']) ? $subNodes['loop'] : array();
+        $this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
+    }
+
+    public function getSubNodeNames() {
+        return array('init', 'cond', 'loop', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Foreach_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Foreach_.php
new file mode 100644
index 00000000000..d2c643205f0
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Foreach_.php
@@ -0,0 +1,43 @@
+ null   : Variable to assign key to
+     *                              'byRef'  => false  : Whether to assign value by reference
+     *                              'stmts'  => array(): Statements
+     * @param array     $attributes Additional attributes
+     */
+    public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->expr = $expr;
+        $this->keyVar = isset($subNodes['keyVar']) ? $subNodes['keyVar'] : null;
+        $this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
+        $this->valueVar = $valueVar;
+        $this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
+    }
+
+    public function getSubNodeNames() {
+        return array('expr', 'keyVar', 'byRef', 'valueVar', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Function_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Function_.php
new file mode 100644
index 00000000000..2df68e5b780
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Function_.php
@@ -0,0 +1,60 @@
+ false  : Whether to return by reference
+     *                           'params'     => array(): Parameters
+     *                           'returnType' => null   : Return type
+     *                           'stmts'      => array(): Statements
+     * @param array  $attributes Additional attributes
+     */
+    public function __construct($name, array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
+        $this->name = $name;
+        $this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
+        $this->returnType = isset($subNodes['returnType']) ? $subNodes['returnType'] : null;
+        $this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
+    }
+
+    public function getSubNodeNames() {
+        return array('byRef', 'name', 'params', 'returnType', 'stmts');
+    }
+
+    public function returnsByRef() {
+        return $this->byRef;
+    }
+
+    public function getParams() {
+        return $this->params;
+    }
+
+    public function getReturnType() {
+        return $this->returnType;
+    }
+
+    public function getStmts() {
+        return $this->stmts;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Global_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Global_.php
new file mode 100644
index 00000000000..29fbc488ee4
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Global_.php
@@ -0,0 +1,26 @@
+vars = $vars;
+    }
+
+    public function getSubNodeNames() {
+        return array('vars');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Goto_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Goto_.php
new file mode 100644
index 00000000000..b087fe02b4f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Goto_.php
@@ -0,0 +1,26 @@
+name = $name;
+    }
+
+    public function getSubNodeNames() {
+        return array('name');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/GroupUse.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/GroupUse.php
new file mode 100644
index 00000000000..d5f46d020d0
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/GroupUse.php
@@ -0,0 +1,35 @@
+type = $type;
+        $this->prefix = $prefix;
+        $this->uses = $uses;
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'prefix', 'uses');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/HaltCompiler.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/HaltCompiler.php
new file mode 100644
index 00000000000..c33ec9f1da5
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/HaltCompiler.php
@@ -0,0 +1,26 @@
+remaining = $remaining;
+    }
+
+    public function getSubNodeNames() {
+        return array('remaining');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/If_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/If_.php
new file mode 100644
index 00000000000..98bda356f62
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/If_.php
@@ -0,0 +1,39 @@
+ array(): Statements
+     *                              'elseifs' => array(): Elseif clauses
+     *                              'else'    => null   : Else clause
+     * @param array     $attributes Additional attributes
+     */
+    public function __construct(Node\Expr $cond, array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->cond = $cond;
+        $this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
+        $this->elseifs = isset($subNodes['elseifs']) ? $subNodes['elseifs'] : array();
+        $this->else = isset($subNodes['else']) ? $subNodes['else'] : null;
+    }
+
+    public function getSubNodeNames() {
+        return array('cond', 'stmts', 'elseifs', 'else');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/InlineHTML.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/InlineHTML.php
new file mode 100644
index 00000000000..accebe61ea7
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/InlineHTML.php
@@ -0,0 +1,26 @@
+value = $value;
+    }
+
+    public function getSubNodeNames() {
+        return array('value');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Interface_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Interface_.php
new file mode 100644
index 00000000000..cb82480d07e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Interface_.php
@@ -0,0 +1,51 @@
+ true,
+        'parent' => true,
+        'static' => true,
+    );
+
+    /**
+     * Constructs a class node.
+     *
+     * @param string $name       Name
+     * @param array  $subNodes   Array of the following optional subnodes:
+     *                           'extends' => array(): Name of extended interfaces
+     *                           'stmts'   => array(): Statements
+     * @param array  $attributes Additional attributes
+     */
+    public function __construct($name, array $subNodes = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->name = $name;
+        $this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : array();
+        $this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
+
+        if (isset(self::$specialNames[strtolower($this->name)])) {
+            throw new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $this->name));
+        }
+
+        foreach ($this->extends as $interface) {
+            if (isset(self::$specialNames[strtolower($interface)])) {
+                throw new Error(
+                    sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
+                    $interface->getAttributes()
+                );
+            }
+        }
+    }
+
+    public function getSubNodeNames() {
+        return array('name', 'extends', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Label.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Label.php
new file mode 100644
index 00000000000..edd0ee9a5ea
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Label.php
@@ -0,0 +1,26 @@
+name = $name;
+    }
+
+    public function getSubNodeNames() {
+        return array('name');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Namespace_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Namespace_.php
new file mode 100644
index 00000000000..fa231214f7b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Namespace_.php
@@ -0,0 +1,52 @@
+ true,
+        'parent' => true,
+        'static' => true,
+    );
+
+    /**
+     * Constructs a namespace node.
+     *
+     * @param null|Node\Name $name       Name
+     * @param null|Node[]    $stmts      Statements
+     * @param array          $attributes Additional attributes
+     */
+    public function __construct(Node\Name $name = null, $stmts = array(), array $attributes = array()) {
+        parent::__construct($attributes);
+        $this->name = $name;
+        $this->stmts = $stmts;
+
+        if (isset(self::$specialNames[strtolower($this->name)])) {
+            throw new Error(
+                sprintf('Cannot use \'%s\' as namespace name', $this->name),
+                $this->name->getAttributes()
+            );
+        }
+
+        if (null !== $this->stmts) {
+            foreach ($this->stmts as $stmt) {
+                if ($stmt instanceof self) {
+                    throw new Error('Namespace declarations cannot be nested', $stmt->getAttributes());
+                }
+            }
+        }
+    }
+
+    public function getSubNodeNames() {
+        return array('name', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Property.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Property.php
new file mode 100644
index 00000000000..a206cc4c9d5
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Property.php
@@ -0,0 +1,56 @@
+type = $type;
+        $this->props = $props;
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'props');
+    }
+
+    public function isPublic() {
+        return ($this->type & Class_::MODIFIER_PUBLIC) !== 0
+            || ($this->type & Class_::VISIBILITY_MODIFER_MASK) === 0;
+    }
+
+    public function isProtected() {
+        return (bool) ($this->type & Class_::MODIFIER_PROTECTED);
+    }
+
+    public function isPrivate() {
+        return (bool) ($this->type & Class_::MODIFIER_PRIVATE);
+    }
+
+    public function isStatic() {
+        return (bool) ($this->type & Class_::MODIFIER_STATIC);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/PropertyProperty.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/PropertyProperty.php
new file mode 100644
index 00000000000..b2d29dc77de
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/PropertyProperty.php
@@ -0,0 +1,30 @@
+name = $name;
+        $this->default = $default;
+    }
+
+    public function getSubNodeNames() {
+        return array('name', 'default');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Return_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Return_.php
new file mode 100644
index 00000000000..b64284114ab
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Return_.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/StaticVar.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/StaticVar.php
new file mode 100644
index 00000000000..4bc5dd25cff
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/StaticVar.php
@@ -0,0 +1,30 @@
+name = $name;
+        $this->default = $default;
+    }
+
+    public function getSubNodeNames() {
+        return array('name', 'default');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Static_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Static_.php
new file mode 100644
index 00000000000..37cc0b36f3e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Static_.php
@@ -0,0 +1,26 @@
+vars = $vars;
+    }
+
+    public function getSubNodeNames() {
+        return array('vars');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Switch_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Switch_.php
new file mode 100644
index 00000000000..72d667b1e37
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Switch_.php
@@ -0,0 +1,30 @@
+cond = $cond;
+        $this->cases = $cases;
+    }
+
+    public function getSubNodeNames() {
+        return array('cond', 'cases');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Throw_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Throw_.php
new file mode 100644
index 00000000000..f8ff6aa3833
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Throw_.php
@@ -0,0 +1,26 @@
+expr = $expr;
+    }
+
+    public function getSubNodeNames() {
+        return array('expr');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUse.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUse.php
new file mode 100644
index 00000000000..a29874bea44
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUse.php
@@ -0,0 +1,31 @@
+traits = $traits;
+        $this->adaptations = $adaptations;
+    }
+
+    public function getSubNodeNames() {
+        return array('traits', 'adaptations');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php
new file mode 100644
index 00000000000..c6038c8ca93
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php
@@ -0,0 +1,13 @@
+trait = $trait;
+        $this->method = $method;
+        $this->newModifier = $newModifier;
+        $this->newName = $newName;
+    }
+
+    public function getSubNodeNames() {
+        return array('trait', 'method', 'newModifier', 'newName');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php
new file mode 100644
index 00000000000..1f6bc1f98d3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php
@@ -0,0 +1,30 @@
+trait = $trait;
+        $this->method = $method;
+        $this->insteadof = $insteadof;
+    }
+
+    public function getSubNodeNames() {
+        return array('trait', 'method', 'insteadof');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Trait_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Trait_.php
new file mode 100644
index 00000000000..356039f31f6
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Trait_.php
@@ -0,0 +1,25 @@
+name = $name;
+        $this->stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('name', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TryCatch.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TryCatch.php
new file mode 100644
index 00000000000..f6d2c84e3ee
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/TryCatch.php
@@ -0,0 +1,39 @@
+stmts = $stmts;
+        $this->catches = $catches;
+        $this->finallyStmts = $finallyStmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('stmts', 'catches', 'finallyStmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Unset_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Unset_.php
new file mode 100644
index 00000000000..0f00fe94155
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Unset_.php
@@ -0,0 +1,26 @@
+vars = $vars;
+    }
+
+    public function getSubNodeNames() {
+        return array('vars');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/UseUse.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/UseUse.php
new file mode 100644
index 00000000000..2508f182775
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/UseUse.php
@@ -0,0 +1,46 @@
+getLast();
+        }
+
+        if ('self' == strtolower($alias) || 'parent' == strtolower($alias)) {
+            throw new Error(sprintf(
+                'Cannot use %s as %s because \'%2$s\' is a special class name',
+                $name, $alias
+            ));
+        }
+
+        parent::__construct($attributes);
+        $this->type = $type;
+        $this->name = $name;
+        $this->alias = $alias;
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'name', 'alias');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Use_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Use_.php
new file mode 100644
index 00000000000..6c89ebb1bee
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/Use_.php
@@ -0,0 +1,43 @@
+type = $type;
+        $this->uses = $uses;
+    }
+
+    public function getSubNodeNames() {
+        return array('type', 'uses');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/While_.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/While_.php
new file mode 100644
index 00000000000..afad1b259ec
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Node/Stmt/While_.php
@@ -0,0 +1,30 @@
+cond = $cond;
+        $this->stmts = $stmts;
+    }
+
+    public function getSubNodeNames() {
+        return array('cond', 'stmts');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeAbstract.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeAbstract.php
new file mode 100644
index 00000000000..ab09bf99543
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeAbstract.php
@@ -0,0 +1,85 @@
+attributes = $attributes;
+    }
+
+    /**
+     * Gets the type of the node.
+     *
+     * @return string Type of the node
+     */
+    public function getType() {
+        return strtr(substr(rtrim(get_class($this), '_'), 15), '\\', '_');
+    }
+
+    /**
+     * Gets line the node started in.
+     *
+     * @return int Line
+     */
+    public function getLine() {
+        return $this->getAttribute('startLine', -1);
+    }
+
+    /**
+     * Sets line the node started in.
+     *
+     * @param int $line Line
+     */
+    public function setLine($line) {
+        $this->setAttribute('startLine', (int) $line);
+    }
+
+    /**
+     * Gets the doc comment of the node.
+     *
+     * The doc comment has to be the last comment associated with the node.
+     *
+     * @return null|Comment\Doc Doc comment object or null
+     */
+    public function getDocComment() {
+        $comments = $this->getAttribute('comments');
+        if (!$comments) {
+            return null;
+        }
+
+        $lastComment = $comments[count($comments) - 1];
+        if (!$lastComment instanceof Comment\Doc) {
+            return null;
+        }
+
+        return $lastComment;
+    }
+
+    public function setAttribute($key, $value) {
+        $this->attributes[$key] = $value;
+    }
+
+    public function hasAttribute($key) {
+        return array_key_exists($key, $this->attributes);
+    }
+
+    public function &getAttribute($key, $default = null) {
+        if (!array_key_exists($key, $this->attributes)) {
+            return $default;
+        } else {
+            return $this->attributes[$key];
+        }
+    }
+
+    public function getAttributes() {
+        return $this->attributes;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeDumper.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeDumper.php
new file mode 100644
index 00000000000..57a6fe7f748
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeDumper.php
@@ -0,0 +1,58 @@
+getType() . '(';
+
+            foreach ($node->getSubNodeNames() as $key) {
+                $r .= "\n    " . $key . ': ';
+
+                $value = $node->$key;
+                if (null === $value) {
+                    $r .= 'null';
+                } elseif (false === $value) {
+                    $r .= 'false';
+                } elseif (true === $value) {
+                    $r .= 'true';
+                } elseif (is_scalar($value)) {
+                    $r .= $value;
+                } else {
+                    $r .= str_replace("\n", "\n    ", $this->dump($value));
+                }
+            }
+        } elseif (is_array($node)) {
+            $r = 'array(';
+
+            foreach ($node as $key => $value) {
+                $r .= "\n    " . $key . ': ';
+
+                if (null === $value) {
+                    $r .= 'null';
+                } elseif (false === $value) {
+                    $r .= 'false';
+                } elseif (true === $value) {
+                    $r .= 'true';
+                } elseif (is_scalar($value)) {
+                    $r .= $value;
+                } else {
+                    $r .= str_replace("\n", "\n    ", $this->dump($value));
+                }
+            }
+        } else {
+            throw new \InvalidArgumentException('Can only dump nodes and arrays.');
+        }
+
+        return $r . "\n)";
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeTraverser.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeTraverser.php
new file mode 100644
index 00000000000..0ddba1a573f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeTraverser.php
@@ -0,0 +1,156 @@
+visitors = array();
+        $this->cloneNodes = $cloneNodes;
+    }
+
+    /**
+     * Adds a visitor.
+     *
+     * @param NodeVisitor $visitor Visitor to add
+     */
+    public function addVisitor(NodeVisitor $visitor) {
+        $this->visitors[] = $visitor;
+    }
+
+    /**
+     * Removes an added visitor.
+     *
+     * @param NodeVisitor $visitor
+     */
+    public function removeVisitor(NodeVisitor $visitor) {
+        foreach ($this->visitors as $index => $storedVisitor) {
+            if ($storedVisitor === $visitor) {
+                unset($this->visitors[$index]);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Traverses an array of nodes using the registered visitors.
+     *
+     * @param Node[] $nodes Array of nodes
+     *
+     * @return Node[] Traversed array of nodes
+     */
+    public function traverse(array $nodes) {
+        foreach ($this->visitors as $visitor) {
+            if (null !== $return = $visitor->beforeTraverse($nodes)) {
+                $nodes = $return;
+            }
+        }
+
+        $nodes = $this->traverseArray($nodes);
+
+        foreach ($this->visitors as $visitor) {
+            if (null !== $return = $visitor->afterTraverse($nodes)) {
+                $nodes = $return;
+            }
+        }
+
+        return $nodes;
+    }
+
+    protected function traverseNode(Node $node) {
+        if ($this->cloneNodes) {
+            $node = clone $node;
+        }
+
+        foreach ($node->getSubNodeNames() as $name) {
+            $subNode =& $node->$name;
+
+            if (is_array($subNode)) {
+                $subNode = $this->traverseArray($subNode);
+            } elseif ($subNode instanceof Node) {
+                $traverseChildren = true;
+                foreach ($this->visitors as $visitor) {
+                    $return = $visitor->enterNode($subNode);
+                    if (self::DONT_TRAVERSE_CHILDREN === $return) {
+                        $traverseChildren = false;
+                    } else if (null !== $return) {
+                        $subNode = $return;
+                    }
+                }
+
+                if ($traverseChildren) {
+                    $subNode = $this->traverseNode($subNode);
+                }
+
+                foreach ($this->visitors as $visitor) {
+                    if (null !== $return = $visitor->leaveNode($subNode)) {
+                        $subNode = $return;
+                    }
+                }
+            }
+        }
+
+        return $node;
+    }
+
+    protected function traverseArray(array $nodes) {
+        $doNodes = array();
+
+        foreach ($nodes as $i => &$node) {
+            if (is_array($node)) {
+                $node = $this->traverseArray($node);
+            } elseif ($node instanceof Node) {
+                $traverseChildren = true;
+                foreach ($this->visitors as $visitor) {
+                    $return = $visitor->enterNode($node);
+                    if (self::DONT_TRAVERSE_CHILDREN === $return) {
+                        $traverseChildren = false;
+                    } else if (null !== $return) {
+                        $node = $return;
+                    }
+                }
+
+                if ($traverseChildren) {
+                    $node = $this->traverseNode($node);
+                }
+
+                foreach ($this->visitors as $visitor) {
+                    $return = $visitor->leaveNode($node);
+
+                    if (self::REMOVE_NODE === $return) {
+                        $doNodes[] = array($i, array());
+                        break;
+                    } elseif (is_array($return)) {
+                        $doNodes[] = array($i, $return);
+                        break;
+                    } elseif (null !== $return) {
+                        $node = $return;
+                    }
+                }
+            }
+        }
+
+        if (!empty($doNodes)) {
+            while (list($i, $replace) = array_pop($doNodes)) {
+                array_splice($nodes, $i, 1, $replace);
+            }
+        }
+
+        return $nodes;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeTraverserInterface.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeTraverserInterface.php
new file mode 100644
index 00000000000..0752de28408
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeTraverserInterface.php
@@ -0,0 +1,48 @@
+ [aliasName => originalName]] */
+    protected $aliases;
+
+    public function beforeTraverse(array $nodes) {
+        $this->resetState();
+    }
+
+    public function enterNode(Node $node) {
+        if ($node instanceof Stmt\Namespace_) {
+            $this->resetState($node->name);
+        } elseif ($node instanceof Stmt\Use_) {
+            foreach ($node->uses as $use) {
+                $this->addAlias($use, $node->type, null);
+            }
+        } elseif ($node instanceof Stmt\GroupUse) {
+            foreach ($node->uses as $use) {
+                $this->addAlias($use, $node->type, $node->prefix);
+            }
+        } elseif ($node instanceof Stmt\Class_) {
+            if (null !== $node->extends) {
+                $node->extends = $this->resolveClassName($node->extends);
+            }
+
+            foreach ($node->implements as &$interface) {
+                $interface = $this->resolveClassName($interface);
+            }
+
+            if (null !== $node->name) {
+                $this->addNamespacedName($node);
+            }
+        } elseif ($node instanceof Stmt\Interface_) {
+            foreach ($node->extends as &$interface) {
+                $interface = $this->resolveClassName($interface);
+            }
+
+            $this->addNamespacedName($node);
+        } elseif ($node instanceof Stmt\Trait_) {
+            $this->addNamespacedName($node);
+        } elseif ($node instanceof Stmt\Function_) {
+            $this->addNamespacedName($node);
+            $this->resolveSignature($node);
+        } elseif ($node instanceof Stmt\ClassMethod
+                  || $node instanceof Expr\Closure
+        ) {
+            $this->resolveSignature($node);
+        } elseif ($node instanceof Stmt\Const_) {
+            foreach ($node->consts as $const) {
+                $this->addNamespacedName($const);
+            }
+        } elseif ($node instanceof Expr\StaticCall
+                  || $node instanceof Expr\StaticPropertyFetch
+                  || $node instanceof Expr\ClassConstFetch
+                  || $node instanceof Expr\New_
+                  || $node instanceof Expr\Instanceof_
+        ) {
+            if ($node->class instanceof Name) {
+                $node->class = $this->resolveClassName($node->class);
+            }
+        } elseif ($node instanceof Stmt\Catch_) {
+            $node->type = $this->resolveClassName($node->type);
+        } elseif ($node instanceof Expr\FuncCall) {
+            if ($node->name instanceof Name) {
+                $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_FUNCTION);
+            }
+        } elseif ($node instanceof Expr\ConstFetch) {
+            $node->name = $this->resolveOtherName($node->name, Stmt\Use_::TYPE_CONSTANT);
+        } elseif ($node instanceof Stmt\TraitUse) {
+            foreach ($node->traits as &$trait) {
+                $trait = $this->resolveClassName($trait);
+            }
+
+            foreach ($node->adaptations as $adaptation) {
+                if (null !== $adaptation->trait) {
+                    $adaptation->trait = $this->resolveClassName($adaptation->trait);
+                }
+
+                if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
+                    foreach ($adaptation->insteadof as &$insteadof) {
+                        $insteadof = $this->resolveClassName($insteadof);
+                    }
+                }
+            }
+
+        }
+    }
+
+    protected function resetState(Name $namespace = null) {
+        $this->namespace = $namespace;
+        $this->aliases   = array(
+            Stmt\Use_::TYPE_NORMAL   => array(),
+            Stmt\Use_::TYPE_FUNCTION => array(),
+            Stmt\Use_::TYPE_CONSTANT => array(),
+        );
+    }
+
+    protected function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) {
+        // Add prefix for group uses
+        $name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
+        // Type is determined either by individual element or whole use declaration
+        $type |= $use->type;
+
+        // Constant names are case sensitive, everything else case insensitive
+        if ($type === Stmt\Use_::TYPE_CONSTANT) {
+            $aliasName = $use->alias;
+        } else {
+            $aliasName = strtolower($use->alias);
+        }
+
+        if (isset($this->aliases[$type][$aliasName])) {
+            $typeStringMap = array(
+                Stmt\Use_::TYPE_NORMAL   => '',
+                Stmt\Use_::TYPE_FUNCTION => 'function ',
+                Stmt\Use_::TYPE_CONSTANT => 'const ',
+            );
+
+            throw new Error(
+                sprintf(
+                    'Cannot use %s%s as %s because the name is already in use',
+                    $typeStringMap[$type], $name, $use->alias
+                ),
+                $use->getLine()
+            );
+        }
+
+        $this->aliases[$type][$aliasName] = $name;
+    }
+
+    /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */
+    private function resolveSignature($node) {
+        foreach ($node->params as $param) {
+            if ($param->type instanceof Name) {
+                $param->type = $this->resolveClassName($param->type);
+            }
+        }
+        if ($node->returnType instanceof Name) {
+            $node->returnType = $this->resolveClassName($node->returnType);
+        }
+    }
+
+    protected function resolveClassName(Name $name) {
+        // don't resolve special class names
+        if (in_array(strtolower($name->toString()), array('self', 'parent', 'static'))) {
+            if (!$name->isUnqualified()) {
+                throw new Error(
+                    sprintf("'\\%s' is an invalid class name", $name->toString()),
+                    $name->getLine()
+                );
+            }
+
+            return $name;
+        }
+
+        // fully qualified names are already resolved
+        if ($name->isFullyQualified()) {
+            return $name;
+        }
+
+        $aliasName = strtolower($name->getFirst());
+        if (!$name->isRelative() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) {
+            // resolve aliases (for non-relative names)
+            $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName];
+            return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
+        }
+
+        if (null !== $this->namespace) {
+            // if no alias exists prepend current namespace
+            return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
+        }
+
+        return new FullyQualified($name->parts, $name->getAttributes());
+    }
+
+    protected function resolveOtherName(Name $name, $type) {
+        // fully qualified names are already resolved
+        if ($name->isFullyQualified()) {
+            return $name;
+        }
+
+        // resolve aliases for qualified names
+        $aliasName = strtolower($name->getFirst());
+        if ($name->isQualified() && isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName])) {
+            $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$aliasName];
+            return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
+        }
+
+        if ($name->isUnqualified()) {
+            if ($type === Stmt\Use_::TYPE_CONSTANT) {
+                // constant aliases are case-sensitive, function aliases case-insensitive
+                $aliasName = $name->getFirst();
+            }
+
+            if (!isset($this->aliases[$type][$aliasName])) {
+                // unqualified, unaliased names cannot be resolved at compile-time
+                return $name;
+            }
+
+            // resolve unqualified aliases
+            return new FullyQualified($this->aliases[$type][$aliasName], $name->getAttributes());
+        }
+
+        if (null !== $this->namespace) {
+            // if no alias exists prepend current namespace
+            return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
+        }
+
+        return new FullyQualified($name->parts, $name->getAttributes());
+    }
+
+    protected function addNamespacedName(Node $node) {
+        if (null !== $this->namespace) {
+            $node->namespacedName = Name::concat($this->namespace, $node->name);
+        } else {
+            $node->namespacedName = new Name($node->name);
+        }
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeVisitorAbstract.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeVisitorAbstract.php
new file mode 100644
index 00000000000..3e1743a74e1
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/NodeVisitorAbstract.php
@@ -0,0 +1,14 @@
+parsers = $parsers;
+        $this->errors = [];
+    }
+
+    public function parse($code) {
+        list($firstStmts, $firstErrors, $firstError) = $this->tryParse($this->parsers[0], $code);
+        if ($firstErrors === []) {
+            $this->errors = [];
+            return $firstStmts;
+        }
+
+        for ($i = 1, $c = count($this->parsers); $i < $c; ++$i) {
+            list($stmts, $errors) = $this->tryParse($this->parsers[$i], $code);
+            if ($errors === []) {
+                $this->errors = [];
+                return $stmts;
+            }
+        }
+
+        $this->errors = $firstErrors;
+        if ($firstError) {
+            throw $firstError;
+        }
+        return $firstStmts;
+    }
+
+    public function getErrors() {
+        return $this->errors;
+    }
+
+    private function tryParse(Parser $parser, $code) {
+        $stmts = null;
+        $error = null;
+        try {
+            $stmts = $parser->parse($code);
+        } catch (Error $error) {}
+        $errors = $parser->getErrors();
+        return [$stmts, $errors, $error];
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Php5.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Php5.php
new file mode 100644
index 00000000000..38c3cd55006
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Php5.php
@@ -0,0 +1,3159 @@
+'",
+        "T_IS_GREATER_OR_EQUAL",
+        "T_SL",
+        "T_SR",
+        "'+'",
+        "'-'",
+        "'.'",
+        "'*'",
+        "'/'",
+        "'%'",
+        "'!'",
+        "T_INSTANCEOF",
+        "'~'",
+        "T_INC",
+        "T_DEC",
+        "T_INT_CAST",
+        "T_DOUBLE_CAST",
+        "T_STRING_CAST",
+        "T_ARRAY_CAST",
+        "T_OBJECT_CAST",
+        "T_BOOL_CAST",
+        "T_UNSET_CAST",
+        "'@'",
+        "T_POW",
+        "'['",
+        "T_NEW",
+        "T_CLONE",
+        "T_EXIT",
+        "T_IF",
+        "T_ELSEIF",
+        "T_ELSE",
+        "T_ENDIF",
+        "T_LNUMBER",
+        "T_DNUMBER",
+        "T_STRING",
+        "T_STRING_VARNAME",
+        "T_VARIABLE",
+        "T_NUM_STRING",
+        "T_INLINE_HTML",
+        "T_ENCAPSED_AND_WHITESPACE",
+        "T_CONSTANT_ENCAPSED_STRING",
+        "T_ECHO",
+        "T_DO",
+        "T_WHILE",
+        "T_ENDWHILE",
+        "T_FOR",
+        "T_ENDFOR",
+        "T_FOREACH",
+        "T_ENDFOREACH",
+        "T_DECLARE",
+        "T_ENDDECLARE",
+        "T_AS",
+        "T_SWITCH",
+        "T_ENDSWITCH",
+        "T_CASE",
+        "T_DEFAULT",
+        "T_BREAK",
+        "T_CONTINUE",
+        "T_GOTO",
+        "T_FUNCTION",
+        "T_CONST",
+        "T_RETURN",
+        "T_TRY",
+        "T_CATCH",
+        "T_FINALLY",
+        "T_THROW",
+        "T_USE",
+        "T_INSTEADOF",
+        "T_GLOBAL",
+        "T_STATIC",
+        "T_ABSTRACT",
+        "T_FINAL",
+        "T_PRIVATE",
+        "T_PROTECTED",
+        "T_PUBLIC",
+        "T_VAR",
+        "T_UNSET",
+        "T_ISSET",
+        "T_EMPTY",
+        "T_HALT_COMPILER",
+        "T_CLASS",
+        "T_TRAIT",
+        "T_INTERFACE",
+        "T_EXTENDS",
+        "T_IMPLEMENTS",
+        "T_OBJECT_OPERATOR",
+        "T_LIST",
+        "T_ARRAY",
+        "T_CALLABLE",
+        "T_CLASS_C",
+        "T_TRAIT_C",
+        "T_METHOD_C",
+        "T_FUNC_C",
+        "T_LINE",
+        "T_FILE",
+        "T_START_HEREDOC",
+        "T_END_HEREDOC",
+        "T_DOLLAR_OPEN_CURLY_BRACES",
+        "T_CURLY_OPEN",
+        "T_PAAMAYIM_NEKUDOTAYIM",
+        "T_NAMESPACE",
+        "T_NS_C",
+        "T_DIR",
+        "T_NS_SEPARATOR",
+        "T_ELLIPSIS",
+        "';'",
+        "'{'",
+        "'}'",
+        "'('",
+        "')'",
+        "'$'",
+        "'`'",
+        "']'",
+        "'\"'"
+    );
+
+    protected $tokenToSymbol = array(
+            0,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,   53,  156,  157,  153,   52,   35,  157,
+          151,  152,   50,   47,    7,   48,   49,   51,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,   29,  148,
+           41,   15,   43,   28,   65,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,   67,  157,  155,   34,  157,  154,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  149,   33,  150,   55,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,    1,    2,    3,    4,
+            5,    6,    8,    9,   10,   11,   12,   13,   14,   16,
+           17,   18,   19,   20,   21,   22,   23,   24,   25,   26,
+           27,   30,   31,   32,   36,   37,   38,   39,   40,   42,
+           44,   45,   46,   54,   56,   57,   58,   59,   60,   61,
+           62,   63,   64,   66,   68,   69,   70,   71,   72,   73,
+           74,   75,   76,   77,   78,   79,   80,   81,  157,  157,
+           82,   83,   84,   85,   86,   87,   88,   89,   90,   91,
+           92,   93,   94,   95,   96,   97,   98,   99,  100,  101,
+          102,  103,  104,  105,  106,  107,  108,  109,  110,  111,
+          112,  113,  114,  115,  116,  117,  118,  119,  120,  121,
+          122,  123,  124,  125,  126,  127,  128,  129,  130,  131,
+          132,  133,  134,  135,  136,  137,  157,  157,  157,  157,
+          157,  157,  138,  139,  140,  141,  142,  143,  144,  145,
+          146,  147
+    );
+
+    protected $action = array(
+          674,  675,  676,  677,  678,-32766,  679,  680,  681,  717,
+          718,  234,  235,  236,  237,  238,  239,  240,  241,  242,
+          456,  243,  244,  245,  246,  247,  248,  249,  250,  251,
+          252,  253,  254,-32766,-32766,-32766,-32766,-32766,-32766,-32766,
+        -32766,-32767,-32767,-32767,-32767,   26,  255,  256,-32766,-32766,
+        -32766,-32766,  682,-32766,    0,-32766,-32766,-32766,-32766,-32766,
+        -32766,-32767,-32767,-32767,-32767,-32767,  683,  684,  685,  686,
+          687,  688,  689,-32766,-32766,  749,-32766,-32766,-32766,-32766,
+        -32766,  324,  690,  691,  692,  693,  694,  695,  696,  697,
+          698,  699,  700,  720,  721,  722,  723,  724,  712,  713,
+          714,  715,  716,  701,  702,  703,  704,  705,  706,  707,
+          743,  744,  745,  746,  747,  748,  708,  709,  710,  711,
+          741,  732,  730,  731,  727,  728,   41,  719,  725,  726,
+          733,  734,  736,  735,  737,  738,   94,   95,   96,  344,
+          457,  729,  740,  739,  822,   70,   71,  438,   72,   73,
+           52,  765,  766,  465,   74,   75,   50,   76,   97,   98,
+           99,  100,  101,  102,  103,  104,  105,  106,  107,  108,
+          109,  110,  111,  112,  113,  114,  115,  116,  117,  118,
+          119,  222,  319,  654,  631, 1211,  919, 1213, 1212,  458,
+           77,   78,  300, -266,  120,  425,   79,  139,   80,  309,
+          310,   81,   82,   83,   84,   85,   86,   87,   88,  647,
+           42,  317,   89,  431,  439,  232,  233,-32766, 1087, 1088,
+          481,  437, 1178,-32766,  812,-32766,  482,   56,   37,  440,
+           45,  483,-32766,  484,  218,  485,  893,  894,  441,-32766,
+        -32766,-32766,   46,   47,  486,  442,  433,   48,  487,  510,
+          632,   90,  443,  443,  367,  368,  446,  923, -217,  313,
+          298,  366,  490,  491,  492,  751,  430, 1178,-32766,-32766,
+        -32766,  900,  495,  496,-32766, 1093, 1094, 1095, 1096, 1090,
+         1091,  325,  446,  231,  232,  233,  510, 1097, 1092,  443,
+          869,  824,  642,   65,   57,-32766,  350,  336,   36,  337,
+         1064,  751,  856,  218,-32766,-32766,  478,  374,-32766,-32766,
+        -32766,  145,-32766,  459,-32766, 1036,-32766,   -1,   67,-32766,
+          231,  232,  233,-32766,-32766,-32766,  446,  316,-32766,-32766,
+          764,  510,-32766,  645,  443,-32766,  509, 1185,  313,  447,
+          218,  350, 1172,-32766,   51,  121,  122,  123,  124,  125,
+          126,  127,  128,  129,  130,  131,  132,-32766,-32766,-32766,
+        -32766,-32766,-32766,-32766,-32766,-32766, 1054,-32767,-32767,-32767,
+        -32767,  142,-32766,-32766,-32766,  135,  899,-32766,-32766,-32766,
+          751,-32766,-32766,-32766,-32766,  975,  977,-32766,-32766,-32766,
+         1036,-32766,   68,-32766, 1208,-32766,  666,  639,-32766, -213,
+          136,-32766,-32766,-32766,-32766,  446,  446,-32766,-32766,  870,
+          137,-32766,  143,  301,-32766,  509,  357,  315,-32766,  352,
+          432,  495,-32766,  231,  232,  233,  335, 1138, 1182, 1036,
+          488,  489,  945,  946,  947,  944,  943,  942,   64,  757,
+          840,  493,  494,  218,  259,  261,  141,  918,  809,-32766,
+          142,-32766,-32766,  339,  810,  751,  224, -494, 1178,-32766,
+          349,  218,-32766,-32766,-32766,  909,-32766, 1137,-32766,  446,
+        -32766,  893,  894,-32766,  138,  659,  356,-32766,-32766,-32766,
+          446,  260,-32766,-32766,  510,  598,-32766,  443,-32766,-32766,
+          509,-32766,-32766,-32766,  751,  255,  256,-32766,-32766,-32766,
+          144,-32766,-32766,-32766,  147,-32766,  225,-32766,  589,-32766,
+         1036,-32766,-32766,-32766,-32766,-32766,-32766,-32766,-32766,  446,
+          134,-32766,-32766,  133,  432,-32766,-32766,-32766,-32766,  509,
+          335, 1172,  350,  311,  488,  489,-32766,  561,-32766,  302,
+          223,   25, 1099,  757,  840,  493,  494,  347,  446, 1036,
+          945,  946,  947,  944,  943,  942,  937,  262,-32766,-32766,
+        -32766,  249,  250,  251,  349,-32766,-32766,  536,  833,-32766,
+          821,  759,  314,  385,  233,  751, 1203,  663,-32766,-32766,
+        -32766,-32766,-32766,-32766,-32766,  146,-32766,  326,-32766,  319,
+        -32766,  658,  218,-32766,  114,  115,  116,-32766,-32766,-32766,
+          446,-32766,-32766,-32766,-32766,  357,-32766,   59,-32766,-32766,
+          509, -208, -208, -208,  751, 1036,  510,-32766,-32766,  443,
+          120,-32766,-32766,-32766, 1099,-32766,   69,-32766,  452,-32766,
+           66,  644,-32766,   62,   49,  312,-32766,-32766,-32766,  446,
+           61,-32766,-32766,-32766,   63,-32766,-32766,-32766,-32766,  509,
+          472,  604,  488,  489,  467,   38,-32766,  148,  375,  765,
+          766,  794,  840,  493,  494,-32767,-32767,-32767,-32767,  112,
+          113,  114,  115,  116,-32766,  611,  612,  231,  232,  233,
+          751,  395,  257,   60,-32766,-32766,-32766,-32766,-32766,-32766,
+          317,-32766,  617,-32766,  530,-32766,  451,  218,-32766,  922,
+          662,  455,-32766,-32766,-32766,  446,  453,-32766,-32766,  758,
+          649,-32766,  752,-32766,-32766,  509,  934,  669,  531,  751,
+          535,  550,-32766,-32766,  551,-32766,-32766,-32766,-32766,  751,
+        -32766, -212,-32766,  778,-32766, 1206, 1036,-32766,  231,  232,
+          233,-32766,-32766,-32766,  446,  614,-32766,-32766,   30,  534,
+        -32766,-32766,-32766,-32766,  509,  624,  432,-32766,  218, 1057,
+          630,-32766,  335,-32766,-32766,-32766,  488,  489, 1036,  541,
+          488,  489,-32766,  -79,  607,  757,  840,  493,  494,  794,
+          840,  493,  494,-32766,  595,-32766,-32766,-32766,-32766,-32766,
+        -32766,-32766,  553,  297, 1080,-32766,  349,  446,  450,  476,
+          608,  751,  348,  760,  346,-32766,  841,  842,-32766,-32766,
+        -32766,  351,-32766, 1098,-32766,  345,-32766,   27,  390,-32766,
+          835, -495,  340,-32766,-32766,-32766,  446,-32766,-32766,-32766,
+          338, -494,-32766,  751,  495,-32766,  509,-32766,-32766, 1144,
+        -32766,-32766,-32766,-32766,-32766,    0,-32766,  402,-32766, 1036,
+           29,-32766,   22,   21, -395,-32766,-32766,-32766,  446, -403,
+        -32766,-32766, -404,  388,-32766,  403,  814,-32766,  509,  411,
+        -32766,  452,  806,-32766,  543,-32766,-32766,-32766,-32766,  427,
+          426, 1036,  488,  489,  928,  863,  819,-32766,-32766,-32766,
+          807,  794,  840,  493,  494,  762,-32766,  761,-32766,-32766,
+        -32766,-32766,-32766,-32766,  802,-32766,  926,-32766,-32766,-32766,
+        -32766,-32766,-32766,  799,  751,  820,  872,   91,-32766, 1038,
+          797,-32766,-32766,-32766,  927,-32766,  930,-32766,  929,-32766,
+          852,  854,-32766,  857,  808,  864,-32766,-32766,-32766,  446,
+        -32766,-32766,-32766,-32766,  805,-32766,  751,  803,-32766,  509,
+        -32766,  813,  763,-32766,-32766,-32766,-32766,-32766,  651,-32766,
+          811,-32766,  650,   55,-32766,   54, 1183,  592,-32766,-32766,
+        -32766,  446,  668,-32766,-32766,  667,  665,-32766,  664,   93,
+        -32766,  509, -198, -198, -198,  804,-32766,  643,-32766,  648,
+          252,  253,  254,  488,  489,  343,  423,  342,  140,  488,
+          489,  424,  794,  840,  493,  494,  255,  256,  794,  840,
+          493,  494,  655,-32766,  656,  661,  653,-32766,-32766,  751,
+          911, 1078, 1175,-32766, 1163, 1177,-32766,-32766,-32766,   40,
+        -32766, 1179,-32766,  258,-32766, 1207, -198,-32766,  471, 1209,
+         1210,-32766,-32766,-32766,  446,  839,-32766,-32766,  769,  770,
+        -32766,  226,  227,-32766,  509,  838,  837,  228, 1050,  229,
+          861,-32766,  862, 1173,  638,  117,  118,  119,  767,  319,
+          768,  220, -206, -206, -206,   43,  226,  227,   44, 1087,
+         1088,  120,  228,-32766,  229,   53,  435, 1089,  488,  489,
+        -32766,-32766, -197, -197, -197,  429,  220,  794,  840,  493,
+          494,  353,  323,  322, 1087, 1088,  321,  320,-32766,  488,
+          489,  308, 1089,  488,  489,  307,  299,  221,  794,  840,
+          493,  494,  794,  840,  493,  494, -214,   92,-32766,   58,
+            0, 1040, -213, 1016,  579,  583, 1093, 1094, 1095, 1096,
+         1090, 1091,  401, 1103,  901, 1044, -197, 1041, 1097, 1092,
+          636,  574,  615,  479,  475,  230,    0,-32766,  473,  579,
+         1015, 1093, 1094, 1095, 1096, 1090, 1091,  401,  468,  396,
+           34,   33,   32, 1097, 1092,  488,  489, 1034, -413,    0,
+          230, 1158,-32766, 1157,  794,  840,  493,  494,  488,  489,
+         1104, 1205,  488,  489, 1077, 1174, 1045,  794,  840,  493,
+          494,  794,  840,  493,  494,  488,  489, 1162, 1176,  488,
+          489, 1063,  836, 1048,  794,  840,  493,  494,  794,  840,
+          493,  494,  488,  489,  621,  827,  488,  489, 1049,  652,
+         1143,  794,  840,  493,  494,  794,  840,  493,  494,  629,
+         1046, 1047,  935,    0,  488,  489, 1039,    0,    0,    0,
+            0,    0,    0,  794,  840,  493,  494,    0,    0,  829,
+            0,  488,  489,  795,    0,    0,    0,    0,    0,    0,
+          794,  840,  493,  494
+    );
+
+    protected $actionCheck = array(
+            2,    3,    4,    5,    6,    8,    8,    9,   10,   11,
+           12,   31,   32,   33,   34,   35,   36,   37,   38,   39,
+           29,   41,   42,   43,   44,   45,   46,   47,   48,   49,
+           50,   51,   52,    8,    9,   10,   31,   32,   33,   34,
+           35,   36,   37,   38,   39,    7,   66,   67,   31,   32,
+           33,   34,   54,   28,    0,   30,   31,   32,   33,   34,
+           35,   36,   37,   38,   39,   40,   68,   69,   70,   71,
+           72,   73,   74,    8,    9,   77,   31,   32,   33,   34,
+           35,    7,   84,   85,   86,   87,   88,   89,   90,   91,
+           92,   93,   94,   95,   96,   97,   98,   99,  100,  101,
+          102,  103,  104,  105,  106,  107,  108,  109,  110,  111,
+          112,  113,  114,  115,  116,  117,  118,  119,  120,  121,
+          122,  123,  124,  125,  126,  127,    7,  129,  130,  131,
+          132,  133,  134,  135,  136,  137,    8,    9,   10,    7,
+          149,  143,  144,  145,    1,    2,    3,    4,    5,    6,
+           13,  102,  103,    7,   11,   12,   28,   14,   30,   31,
+           32,   33,   34,   35,   36,   37,   38,   39,   40,   41,
+           42,   43,   44,   45,   46,   47,   48,   49,   50,   51,
+           52,    7,   54,   29,   77,   77,  148,   79,   80,    7,
+           47,   48,    7,   79,   66,  146,   53,    7,   55,   56,
+           57,   58,   59,   60,   61,   62,   63,   64,   65,   29,
+           67,   68,   69,   70,   71,    9,   10,  103,   75,   76,
+           77,    7,   79,  109,   81,    1,   83,   84,   85,   86,
+            7,   88,  118,   90,   28,   92,  130,  131,   95,    8,
+            9,   10,   99,  100,  101,  102,  123,  104,  105,  143,
+          143,  108,  146,  146,  111,  112,  102,  150,  152,   35,
+           13,    7,  119,  120,  121,   77,    7,   79,   31,   32,
+           33,  152,  129,  130,  151,  132,  133,  134,  135,  136,
+          137,  138,  102,    8,    9,   10,  143,  144,  145,  146,
+           29,  148,  149,   67,  151,   71,  153,  154,  152,  156,
+          112,   77,  148,   28,    1,   81,    7,    7,   84,   85,
+           86,   67,   88,    7,   90,   12,   92,    0,   67,   95,
+            8,    9,   10,   99,  100,  101,  102,   35,  104,  105,
+          148,  143,  108,   29,  146,  111,  112,  152,   35,  151,
+           28,  153,   79,  119,   15,   16,   17,   18,   19,   20,
+           21,   22,   23,   24,   25,   26,   27,   31,   32,   33,
+           34,   35,   36,   37,   38,   39,  152,   41,   42,   43,
+           44,  147,  148,  149,   71,  149,  152,   31,   32,    1,
+           77,    8,    9,   10,   81,   56,   57,   84,   85,   86,
+           12,   88,   67,   90,  150,   92,   29,   77,   95,  152,
+          149,   28,   99,  100,  101,  102,  102,  104,  105,  148,
+           13,  108,  149,   35,  111,  112,  153,    7,    1,    7,
+          103,  129,  119,    8,    9,   10,  109,  152,   77,   12,
+          113,  114,  112,  113,  114,  115,  116,  117,   67,  122,
+          123,  124,  125,   28,   29,   15,   29,  148,  148,   71,
+          147,  148,  149,  128,  148,   77,   15,  128,   79,   81,
+          143,   28,   84,   85,   86,   79,   88,  155,   90,  102,
+           92,  130,  131,   95,  149,   29,   67,   99,  100,  101,
+          102,   15,  104,  105,  143,   82,  108,  146,   71,  111,
+          112,    8,    9,   10,   77,   66,   67,  119,   81,    1,
+          149,   84,   85,   86,  149,   88,   15,   90,  153,   92,
+           12,   28,   95,   30,   31,   32,   99,  100,  101,  102,
+          149,  104,  105,   15,  103,  108,  148,  149,  111,  112,
+          109,   79,  153,   35,  113,  114,  119,  128,    1,  153,
+           15,  103,  139,  122,  123,  124,  125,  109,  102,   12,
+          112,  113,  114,  115,  116,  117,  118,   15,    8,    9,
+           10,   47,   48,   49,  143,  148,  149,   82,   35,   71,
+           29,  150,   35,   79,   10,   77,   82,   29,   28,   81,
+           30,   31,   84,   85,   86,   29,   88,   29,   90,   54,
+           92,   29,   28,   95,   47,   48,   49,   99,  100,  101,
+          102,   31,  104,  105,    1,  153,  108,   67,   71,  111,
+          112,   72,   73,   74,   77,   12,  143,  119,   81,  146,
+           66,   84,   85,   86,  139,   88,   67,   90,  146,   92,
+           67,  149,   95,   67,  140,  141,   99,  100,  101,  102,
+           67,  104,  105,    1,   67,  108,  148,  149,  111,  112,
+           72,   73,  113,  114,   72,   73,  119,   97,   98,  102,
+          103,  122,  123,  124,  125,   41,   42,   43,   44,   45,
+           46,   47,   48,   49,   71,  106,  107,    8,    9,   10,
+           77,   78,   13,   67,   81,  148,  149,   84,   85,   86,
+           68,   88,   74,   90,   77,   92,   77,   28,   95,  148,
+          149,   86,   99,  100,  101,  102,   77,  104,  105,  148,
+          149,  108,   77,   71,  111,  112,  148,  149,   77,   77,
+           77,   77,  119,   81,   77,    1,   84,   85,   86,   77,
+           88,   74,   90,   77,   92,   77,   12,   95,    8,    9,
+           10,   99,  100,  101,  102,   79,  104,  105,   94,   79,
+          108,  148,  149,  111,  112,   79,  103,    1,   28,   79,
+           79,  119,  109,    8,    9,   10,  113,  114,   12,   82,
+          113,  114,   82,   94,   96,  122,  123,  124,  125,  122,
+          123,  124,  125,   28,   96,   30,   31,   32,   33,   34,
+          148,  149,   94,   94,  152,   71,  143,  102,  102,  102,
+          109,   77,  110,  150,  127,   81,  123,  123,   84,   85,
+           86,  126,   88,  139,   90,  126,   92,  142,  146,   95,
+          147,  128,  128,   99,  100,  101,  102,   71,  104,  105,
+          128,  128,  108,   77,  129,  111,  112,   81,    1,  139,
+           84,   85,   86,  119,   88,   -1,   90,  146,   92,   12,
+          142,   95,  142,  142,  142,   99,  100,  101,  102,  142,
+          104,  105,  142,  142,  108,  146,  148,  111,  112,  146,
+            1,  146,  148,  149,  146,  119,    8,    9,   10,  146,
+          146,   12,  113,  114,  148,  148,  148,    8,    9,   10,
+          148,  122,  123,  124,  125,  148,   28,  148,   30,   31,
+           32,   33,   34,   35,  148,  149,  148,   28,   71,   30,
+           31,   32,   33,  148,   77,  148,  148,  151,   81,  150,
+          148,   84,   85,   86,  148,   88,  148,   90,  148,   92,
+          148,  148,   95,  148,  148,  148,   99,  100,  101,  102,
+           71,  104,  105,    1,  148,  108,   77,  148,  111,  112,
+           81,  148,  148,   84,   85,   86,  119,   88,  148,   90,
+          148,   92,  148,  148,   95,  148,  150,   87,   99,  100,
+          101,  102,  149,  104,  105,  149,  149,  108,  149,  149,
+          111,  112,   96,   97,   98,  148,  149,  149,  119,  149,
+           50,   51,   52,  113,  114,  149,  149,  149,  149,  113,
+          114,  149,  122,  123,  124,  125,   66,   67,  122,  123,
+          124,  125,  149,   71,  149,  149,  149,  148,  149,   77,
+          150,  150,  150,   81,  150,  150,   84,   85,   86,  151,
+           88,  150,   90,   29,   92,  150,  150,   95,  150,  150,
+          150,   99,  100,  101,  102,  150,  104,  105,  150,  150,
+          108,   47,   48,  111,  112,  150,  150,   53,  150,   55,
+          150,  119,  150,  150,   89,   50,   51,   52,  150,   54,
+          150,   67,   72,   73,   74,  151,   47,   48,  151,   75,
+           76,   66,   53,   79,   55,  151,  151,   83,  113,  114,
+          148,  149,   96,   97,   98,  151,   67,  122,  123,  124,
+          125,  151,  151,  151,   75,   76,  151,  151,   79,  113,
+          114,  151,   83,  113,  114,  151,  151,  151,  122,  123,
+          124,  125,  122,  123,  124,  125,  152,  151,  151,  151,
+           -1,  152,  152,  152,  130,  152,  132,  133,  134,  135,
+          136,  137,  138,  152,  152,  152,  150,  152,  144,  145,
+          152,  152,  155,  152,  152,  151,   -1,  153,  152,  130,
+          152,  132,  133,  134,  135,  136,  137,  138,  152,  152,
+          152,  152,  152,  144,  145,  113,  114,  154,  154,   -1,
+          151,  155,  153,  155,  122,  123,  124,  125,  113,  114,
+          155,  155,  113,  114,  155,  155,  155,  122,  123,  124,
+          125,  122,  123,  124,  125,  113,  114,  155,  155,  113,
+          114,  155,  150,  155,  122,  123,  124,  125,  122,  123,
+          124,  125,  113,  114,   93,  150,  113,  114,  155,  150,
+          156,  122,  123,  124,  125,  122,  123,  124,  125,   91,
+          155,  155,  150,   -1,  113,  114,  150,   -1,   -1,   -1,
+           -1,   -1,   -1,  122,  123,  124,  125,   -1,   -1,  150,
+           -1,  113,  114,  150,   -1,   -1,   -1,   -1,   -1,   -1,
+          122,  123,  124,  125
+    );
+
+    protected $actionBase = array(
+            0,  886,  996,  317,  653, 1000,  421,  539, 1113, 1079,
+          880, 1131,  657, 1148, 1109, 1062,  769,  975, 1096, 1075,
+         1092,  263,  263,  107,  736,   -2,   -2,   -2,   -2,   -2,
+          320,  180,  446,  367,  446,  304,  154,  695,  695,  695,
+          224,  303,  498,  498,  642,  498,  756,  837,  724,  603,
+          417,  378,  537,  869,  869,  869,  869,  942,  942,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  869,
+          869,  869,  869,  869,  869,  869,  869,  869,  869,  261,
+          803,  397,  137,  799,  796,  742,  738,  979,  699,  981,
+          885,  881,  556,  875,  874,  872,  871,  870,  888,  816,
+          997,  913,  128,  128,  128,  128,  128,  128,  128,  128,
+          128,  128,  128,   -3,  669,  275,  415,  312,   65,  730,
+          730,  730,  730,  730,  730,  730,  231,  231,  231,  231,
+          231,  231,  231,  231,  231,  231,  231,  231,  231,  231,
+          231,  231,  231,  206,  373,  373,  373,  564, 1004,  433,
+         1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029,
+         1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029,
+         1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029,
+         1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029,
+         1029, 1029, 1029,  550,  -20,  -20,  483,  570,  879,  346,
+          755,  237,  868,   25,   25,   25,   25,   25,   17,   45,
+            5,    5,    5,    5,  329,  624,  624,  624,  624,  326,
+          326,  326,  326,  895,  905,  906,  908,  292,  292,  705,
+          705,  622,  849,  547,  547,  514,  514,  188,  188,  188,
+          188,  188,  188,  188,  188,  188,  188,  123,  114,  977,
+          106,  106,  106,  106,  341,  485,  341,  700, 1074, 1015,
+         1015, 1015,  940,  940,  940,   49,  687,  494,  351,  351,
+          351,  494,  557,  557,  557,  473,  473,  473,  473,  482,
+          830,  473,  473,  473,  226,  690,  108,  355,  688,  910,
+          678,  912,  551,  720,  325,  734,  733,  386,  696,  582,
+          578,  569,  712,   -9,  386,  261,  568,  251,  535,  812,
+          647,  182,  749,  300,  306,  409,  561,  371,  452,  146,
+          848,  747,  983, 1008,  185,  244,  693,  535,  535,  535,
+          119,  403,  846,  847,  371,  247,  429,  429,  429,  429,
+          898,  852,  429,  429,  429,  429,  899,  918,   38,  299,
+          920,  214,  758,  652,  652,  652,  652,  652,  652,  533,
+          652,  927,  641,  878,  878,  644,  666,  533,  978,  978,
+          978,  978,  533,  652,  878,  878,  533,  622,  878,  223,
+          533,  689,  652,  685,  685,  978,  814,  810,  641,  670,
+          677,  878,  878,  878,  677,  644,  533,  978,  691,  673,
+          174,  878,  978,  558,  558,  691,  533,  558,  666,  558,
+           54,  541,  540,  934,  766,  935,  617,  838,  683,  684,
+          964,  960,  976,  643,  619,  965,  924,  728,  804,  786,
+          629,  508,  616,  573,  566,  577,  711,  563,  702,  696,
+          725,  554,  554,  554,  703,  710,  703,  554,  554,  554,
+          554,  554,  554,  554,  554, 1041,  721,  717,  694,  615,
+          767,  525,  672,  698,  441,  840,  674,  728,  728,  995,
+         1017, 1024, 1023,  635,  956,  999,  703, 1086,  817,  190,
+          491,  955,  701,  656,  719,  703,  952,  703,  867,  703,
+          993,  658,  890,  728,  554,  992, 1085, 1073, 1058, 1056,
+         1053, 1052, 1026, 1040,  559, 1039,  765, 1016,   74,  966,
+          712,  675,  723,  718,  412, 1036,  703,  703,  863,  830,
+          703,  865,  768,  815, 1020,  737, 1006, 1035,  674, 1002,
+          703,  697, 1028,  412,  562,  618,  974,  787,  944,  676,
+          991,  950,  866,  466,  430,  889,  548,  772, 1019, 1018,
+          980,  785,  829,  827,  542,  680,  681,  826,  951,  783,
+          998,  679,  725,  708,  692,  654,  823, 1001,  782,  780,
+          778,  776,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,   -2,   -2,   -2,   -2,    0,    0,    0,    0,    0,
+           -2,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
+          143,  143,  143,  143,  143,  143,  143,  143,  143,  128,
+          128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+          128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+          128,  128,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,  128,  -20,  -20,  -20,  -20,  128,  -20,
+          -20,  -20,  -20,  -20,  -20,  -20,  128,  128,  128,  128,
+          128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+          128,  128,  128,  -20,  128,  128,  128,  -20,  188,  -20,
+          188,  188,  188,  188,  188,  188,  188,  188,  188,  188,
+          188,  188,  188,  188,  188,  188,  188,  188,  188,  188,
+          188,  188,  188,  188,  188,  188,  188,  188,  188,  188,
+          188,  188,  188,  188,  188,  188,  188,  188,  188,  188,
+          188,  188,  188,  128,    0,    0,  128,  -20,  128,  -20,
+          128,  -20,  128,  128,  128,  128,  128,  128,  -20,  -20,
+          -20,  -20,  -20,  -20,    0, 1015, 1015, 1015, 1015,  -20,
+          -20,  -20,  -20,  438,  438,  438,  438,  188,  188,  188,
+          188,  188,  188, 1015, 1015,  940,  940,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,  188,  438,  188,
+          652,  652,  652,  652,  652,  494,  652,  494,  494,    0,
+            0,    0,    0,    0,    0,  652,  494,    0,  379,  379,
+          379,    0,  652,  652,  652,  652,  652,  652,  652,  652,
+          379,  652,  652,  652,  878,  494,    0,  379,  560,  560,
+          560,  560,  412,  371,    0,  652,  652,    0,  670,    0,
+            0,    0,  878,    0,    0,    0,    0,    0,  554,  190,
+          956,    0,  254,    0,    0,    0,    0,    0,    0,    0,
+          635,  254,  410,  410,    0,    0,  559,  554,  554,  554,
+            0,    0,  635,  635,    0,    0,    0,    0,    0,    0,
+          132,  635,    0,    0,    0,    0,  132,  259,    0,    0,
+          259,    0,  412
+    );
+
+    protected $actionDefault = array(
+            3,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,  521,  521,32767,  478,32767,32767,32767,32767,
+        32767,32767,32767,  284,  284,  284,32767,32767,32767,  510,
+          510,  510,  510,  510,  510,  510,  510,  510,  510,  510,
+        32767,32767,32767,32767,32767,  366,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,  372,  526,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,  347,  348,  350,  351,  283,  511,  234,  373,
+          525,  282,  236,  311,  482,32767,32767,32767,  313,  115,
+          245,  190,  481,  118,  281,  221,  365,  367,  312,  288,
+          293,  294,  295,  296,  297,  298,  299,  300,  301,  302,
+          303,  304,  287,  438,  344,  343,  342,  440,32767,  439,
+          475,  475,  478,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,  309,  466,  465,  310,  436,  314,  437,
+          316,  441,  315,  332,  333,  330,  331,  334,  443,  442,
+          459,  460,  457,  458,  286,  335,  336,  337,  338,  461,
+          462,  463,  464,  268,  268,  268,  268,32767,32767,  520,
+          520,32767,32767,  323,  324,  450,  451,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,  269,32767,
+          225,  225,  225,  225,  225,32767,32767,32767,32767,  318,
+          319,  317,  445,  446,  444,32767,  412,32767,32767,32767,
+        32767,  414,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,  483,32767,32767,32767,32767,32767,
+        32767,32767,32767,  496,  401,32767,32767,32767,  394,  209,
+          211,  158,  469,32767,32767,32767,32767,  501,  328,32767,
+        32767,32767,32767,32767,32767,  534,32767,  496,32767,32767,
+        32767,32767,32767,32767,32767,32767,  341,  320,  321,  322,
+        32767,32767,32767,32767,  500,  494,  453,  454,  455,  456,
+        32767,32767,  447,  448,  449,  452,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,  162,
+        32767,  409,32767,  415,  415,32767,32767,  162,32767,32767,
+        32767,32767,  162,32767,  499,  498,  162,32767,  395,  477,
+          162,  175,32767,  173,  173,32767,  195,  195,32767,32767,
+          177,  470,  489,32767,  177,32767,  162,32767,  383,  164,
+          477,32767,32767,  227,  227,  383,  162,  227,32767,  227,
+        32767,   81,  419,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,  396,32767,32767,
+        32767,32767,  362,  363,  472,  485,32767,  486,32767,  394,
+        32767,  326,  327,  329,  306,32767,  308,  352,  353,  354,
+          355,  356,  357,  358,  360,32767,32767,  399,  402,32767,
+        32767,32767,   83,  107,  244,32767,  533,   83,  397,32767,
+          291,  533,32767,32767,32767,32767,  528,32767,32767,  285,
+        32767,32767,   83,32767,   83,  240,32767,  160,32767,  518,
+        32767,32767,  494,  398,  325,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,  495,32767,32767,32767,  216,32767,
+          432,32767,   83,32767,  176,32767,  289,  235,32767,32767,
+          527,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+          161,32767,32767,  178,32767,32767,  494,32767,32767,32767,
+        32767,32767,32767,32767,  280,32767,32767,32767,32767,32767,
+          494,32767,32767,32767,  220,32767,32767,32767,32767,32767,
+        32767,   81,   59,32767,  262,32767,32767,32767,32767,32767,
+        32767,32767,  120,  120,    3,  120,  203,  120,  247,    3,
+          195,  195,  155,  247,  120,  247,  247,  120,  120,  120,
+          120,  120,  254,  120,  120,  120,  120,  120,  120,  120
+    );
+
+    protected $goto = array(
+          178,  178,  152,  152,  157,  152,  153,  154,  155,  160,
+          162,  199,  180,  176,  176,  176,  176,  157,  157,  177,
+          177,  177,  177,  177,  177,  177,  177,  177,  177,  177,
+          172,  173,  174,  175,  196,  151,  197,  511,  512,  378,
+          513,  517,  518,  519,  520,  521,  522,  523,  524,  962,
+          156,  158,  159,  161,  183,  188,  198,  214,  263,  266,
+          268,  270,  272,  273,  274,  275,  276,  277,  285,  286,
+          287,  288,  303,  304,  329,  330,  331,  397,  398,  399,
+          564,  200,  201,  202,  203,  204,  205,  206,  207,  208,
+          209,  210,  211,  212,  163,  164,  165,  179,  166,  181,
+          167,  215,  182,  168,  169,  170,  216,  171,  149,  633,
+          581,  786,  581,  581,  581,  581,  581,  581,  581,  581,
+          581,  581,  581,  581,  581,  581,  581,  581,  581,  581,
+          581,  581,  581,  581,  581,  581,  581,  581,  581,  581,
+          581,  581,  581,  581,  581,  581,  581,  581,  581,  581,
+          581,  581,  581,  581,  581, 1100,   24, 1100, 1100, 1100,
+         1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100,
+         1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100,
+         1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100,
+         1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100,
+          885,  885, 1189, 1189,  528,  623,  528,  186,  627,  527,
+          376,  527,  189,  190,  191,  406,  407,  408,  409,  185,
+          213,  217,  219,  267,  269,  271,  278,  279,  280,  281,
+          282,  283,  289,  290,  291,  292,  305,  306,  332,  333,
+          334,  412,  413,  414,  415,  187,  192,  264,  265,  193,
+          194,  195,  515,  515,  515,  515,  515,  515, 1169, 1168,
+         1169, 1168,  515,  515,  515,  515,  515,  515,  515,  515,
+          515,  515,  526,    9,  526,    5,  540,   10,    4,  640,
+         1184, 1184, 1184,    6,   11,  593,  618,    1,   12,   13,
+            2,   14, 1167,    7,   15,   16,   17,   18,   19,   20,
+          756,  756,  816,  756,  580,  855,  580,  580,  580,  580,
+          580,  580,  580,  580,  580,  580,  580,  580,  580,  580,
+          580,  580,  580,  580,  580,  580,  580,  580,  580,  580,
+          580,  580,  580,  580,  580,  580,  580,  580,  580,  580,
+          580,  580,  580,  580,  580,  580,  580,  580,  580,  566,
+          567,  568,  569,  570,  571,  572,  573,  575,  602,  532,
+          294,  359,  295,  296, 1199, 1199,  537,  537,  537,  461,
+          463,  933,  641,  537,  903, 1101,  628,  931, 1199,  755,
+          755,  560,  755,  448,  448,  448,  448,  448,  448,  554,
+          537, 1193, 1202,  448,  448,  448,  448,  448,  448,  448,
+          448,  448,  448, 1065, 1161, 1065,  892,  892,  892,  892,
+          892,  779,  892,  596,  599,  637,  379,  387,  387,  387,
+          341,  328, 1058,  610,  603,  362,  422,  754,  754,  387,
+          754,  849,  849,  849,  849, 1149,  404,  844,  850,  626,
+          619,  779,  779, 1186,  941, 1076, 1075,  537,  537,  552,
+          582,  537,  537,  393,  537,  871,  853,  851,  853,  646,
+         1009,  529,  880,  875,  889,  898,  365,  538,  405,  620,
+          559,  559,  586,  555, 1200, 1200,  563,  416,  557,  557,
+          514,  516,  546,  562,  587,  590,  600,  606, 1200,  391,
+          775,  773,  565,  386, 1061, 1062,  539,  907, 1058,  772,
+          772,  780,  780,  780,  782,  657,  771,  420, 1081,  539,
+          539, 1059, 1160, 1059,  464, 1019,   35,   31,  373, 1056,
+          960, 1060,  380, 1051,  783,  591,  544,  912,  859, 1146,
+          556,  477,  576,  949,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,   39,    0,  469,    0,    0,  616,
+            0,    0,    0,    0,  360,  361,   28,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,  545
+    );
+
+    protected $gotoCheck = array(
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   51,
+          110,   24,  110,  110,  110,  110,  110,  110,  110,  110,
+          110,  110,  110,  110,  110,  110,  110,  110,  110,  110,
+          110,  110,  110,  110,  110,  110,  110,  110,  110,  110,
+          110,  110,  110,  110,  110,  110,  110,  110,  110,  110,
+          110,  110,  110,  110,  110,  117,   88,  117,  117,  117,
+          117,  117,  117,  117,  117,  117,  117,  117,  117,  117,
+          117,  117,  117,  117,  117,  117,  117,  117,  117,  117,
+          117,  117,  117,  117,  117,  117,  117,  117,  117,  117,
+          117,  117,  117,  117,  117,  117,  117,  117,  117,  117,
+           68,   68,   68,   68,  113,   55,  113,   22,   55,  110,
+           55,  110,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,  107,  107,  107,  107,  107,  107,  108,  109,
+          108,  109,  107,  107,  107,  107,  107,  107,  107,  107,
+          107,  107,  107,   25,  107,   25,   91,   25,    2,    4,
+          109,  109,  109,    2,   25,   34,   34,   25,   25,   25,
+           25,   25,  109,   25,   25,   25,   25,   25,   25,   25,
+           11,   11,   44,   11,   51,   27,   51,   51,   51,   51,
+           51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+           51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+           51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+           51,   51,   51,   51,   51,   51,   51,   51,   51,  100,
+          100,  100,  100,  100,  100,  100,  100,  100,  100,    7,
+           59,   63,   59,   59,  131,  131,    7,    7,    7,    6,
+            6,    6,    6,    7,   75,    6,    6,    6,  131,   10,
+           10,   99,   10,   51,   51,   51,   51,   51,   51,    7,
+            7,  130,  131,   51,   51,   51,   51,   51,   51,   51,
+           51,   51,   51,   51,   73,   51,   51,   51,   51,   51,
+           51,   18,   51,   54,   54,   54,   41,  114,  114,  114,
+          116,  116,   73,  118,   61,   61,   61,    9,    9,  114,
+            9,   51,   51,   51,   51,  122,  114,   51,   51,   51,
+           43,   18,   18,  128,   88,  115,  115,    7,    7,    7,
+            7,    7,    7,   42,    7,    9,    9,    9,    9,    9,
+           92,    9,    9,    9,   70,   72,   13,    7,   45,   45,
+           45,   45,   45,   45,  132,  132,    7,   17,   45,   45,
+           45,   45,   45,   45,   45,   45,   45,   45,  132,   12,
+           20,   19,   38,    8,   73,   73,   38,   76,   73,   18,
+           18,   18,   18,   18,   18,   65,   18,   16,   28,   38,
+           38,   73,   73,   73,   57,   28,   28,   28,   28,  105,
+           91,   73,   52,  103,   21,   58,   52,   77,   62,  121,
+           52,   98,   28,   90,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   28,   -1,   52,   -1,   -1,   28,
+           -1,   -1,   -1,   -1,   63,   63,   52,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   91
+    );
+
+    protected $gotoBase = array(
+            0,    0, -366,    0,  249,    0,  348,   24,  144,  424,
+          376,  297,  124,  131,    0,    0,   83,  134,   76,  125,
+          146,   66,  -11,    0,   85, -370,    0,  269,   77,    0,
+            0,    0,    0,    0,  231,    0,    0,  -40,  452,    0,
+            0,  360,  145,  142,  268,  171,    0,    0,    0,    0,
+            0,   86,   80,    0,   92, -269,    0,   60,   61, -293,
+            0,   78,   63,  -96,    0,  130,    0,    0,  -97,    0,
+          140,    0,  139,   50,    0,  333,  123,   68,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,  126,    0,
+           55,  219,  141,    0,    0,    0,    0,    0,   56,  338,
+          289,    0,    0,   89,    0,   82,    0,  -45,  -59,  -58,
+         -108,    0,    0, -113,   79,  106,   84,  -63,  202,    0,
+            0,   59,  213,    0,    0,    0,    0,    0,  143,    0,
+          346,   37,  147,    0
+    );
+
+    protected $gotoDefault = array(
+        -32768,  480,    3,  672,  742,  750,  613,  497,  533,  791,
+          792,  793,  382,  428,  498,  381,  417,  410,  781,  774,
+          776,  784,  184,  418,  787,    8,  789,  823, 1010,  369,
+          796,  370,  605,  798,  548,  800,  801,  150,  499,  383,
+          384,  549,  392,  594,  815,  284,  389,  817,  371,  818,
+          826,  372,  625,  609,  577,  622,  500,  460,  588,  293,
+          558,  584,  858,  358,  866,  660,  874,  877,  501,  578,
+          888,  466,  896, 1086,  400,  902,  908,  913,  916,  436,
+          419,  601,  920,  921,   23,  925,  634,  635,  940,  318,
+          948,  961,  434, 1029, 1031,  502,  503,  542,  474,  525,
+          547,  504, 1052,  454,  421, 1055,  505,  506,  444,  445,
+         1073, 1070,  364, 1154,  363,  462,  327, 1141,  597, 1105,
+          470, 1192, 1150,  354,  507,  508,  377,  394, 1187,  449,
+         1194, 1201,  355,  585
+    );
+
+    protected $ruleToNonTerminal = array(
+            0,    1,    2,    2,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    5,    5,    5,    5,    5,    5,    5,    6,
+            6,    7,    7,    8,    3,    3,    3,    3,    3,    3,
+            3,    3,    3,    3,    3,   13,   13,   14,   14,   14,
+           14,   16,   16,   12,   12,   17,   17,   18,   18,   19,
+           19,   20,   20,   15,   15,   21,   23,   23,   24,   25,
+           25,   26,   26,   26,   26,   27,   27,   27,   27,   27,
+           27,   27,   27,   27,   27,   27,   27,   27,   27,   27,
+           27,   27,   27,   27,   27,   27,   27,   27,   27,   27,
+           27,   27,   27,    9,    9,   48,   48,   50,   49,   49,
+           42,   42,   52,   52,   53,   53,   10,   11,   11,   11,
+           56,   56,   56,   57,   57,   60,   60,   58,   58,   61,
+           61,   35,   35,   44,   44,   47,   47,   47,   46,   46,
+           62,   36,   36,   36,   36,   63,   63,   64,   64,   65,
+           65,   33,   33,   29,   29,   66,   31,   31,   67,   30,
+           30,   32,   32,   43,   43,   43,   54,   54,   69,   69,
+           70,   70,   72,   72,   72,   71,   71,   55,   55,   73,
+           73,   73,   74,   74,   75,   75,   75,   39,   39,   76,
+           76,   76,   40,   40,   77,   77,   59,   59,   78,   78,
+           78,   78,   83,   83,   84,   84,   85,   85,   85,   85,
+           85,   86,   87,   87,   82,   82,   79,   79,   81,   81,
+           89,   89,   88,   88,   88,   88,   88,   88,   80,   80,
+           90,   90,   41,   41,   34,   34,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
+           28,   28,   38,   38,   95,   95,   96,   96,   96,   96,
+          102,   91,   91,   98,   98,  104,  104,  105,  106,  106,
+          106,  106,  106,  106,  110,  110,   51,   51,   51,   92,
+           92,  111,  111,  107,  107,  112,  112,  112,  112,   93,
+           93,   93,   97,   97,   97,  103,  103,  117,  117,  117,
+          117,  117,  117,  117,  117,  117,  117,  117,  117,  117,
+           22,   22,   22,   22,   22,   22,  119,  119,  119,  119,
+          119,  119,  119,  119,  119,  119,  119,  119,  119,  119,
+          119,  119,  119,  119,  119,  119,  119,  119,  119,  119,
+          119,  119,  119,  119,  119,  119,  119,  119,  119,  101,
+          101,   94,   94,   94,   94,  118,  118,  121,  121,  120,
+          120,  122,  122,   45,   45,   45,   45,  124,  124,  123,
+          123,  123,  123,  123,  125,  125,  109,  109,  113,  113,
+          108,  108,  126,  126,  126,  126,  114,  114,  114,  114,
+          100,  100,  115,  115,  115,   68,  127,  127,  128,  128,
+          128,   99,   99,  129,  129,  130,  130,  130,  130,  116,
+          116,  116,  116,  132,  131,  131,  131,  131,  131,  131,
+          131,  133,  133,  133
+    );
+
+    protected $ruleToLength = array(
+            1,    1,    2,    0,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    3,    1,    1,    1,    1,    1,    3,    5,
+            4,    3,    4,    2,    3,    1,    1,    7,    8,    6,
+            7,    3,    1,    3,    1,    3,    1,    1,    3,    1,
+            2,    1,    2,    3,    1,    3,    3,    1,    3,    2,
+            0,    1,    1,    1,    1,    3,    5,    8,    3,    5,
+            9,    3,    2,    3,    2,    3,    2,    3,    2,    3,
+            3,    3,    1,    2,    5,    7,    9,    5,    6,    3,
+            3,    2,    1,    1,    1,    0,    2,    8,    0,    4,
+            1,    3,    0,    1,    0,    1,   10,    7,    6,    5,
+            1,    2,    2,    0,    2,    0,    2,    0,    2,    1,
+            3,    1,    4,    1,    4,    1,    1,    4,    1,    3,
+            3,    3,    4,    4,    5,    0,    2,    4,    3,    1,
+            1,    1,    4,    0,    2,    3,    0,    2,    4,    0,
+            2,    0,    3,    1,    2,    1,    1,    0,    1,    3,
+            4,    6,    1,    1,    1,    0,    1,    0,    2,    2,
+            3,    3,    1,    3,    1,    2,    2,    3,    1,    1,
+            2,    4,    3,    1,    1,    3,    2,    0,    3,    3,
+            9,    3,    1,    3,    0,    2,    4,    5,    4,    4,
+            4,    3,    1,    1,    1,    3,    1,    1,    0,    1,
+            1,    2,    1,    1,    1,    1,    1,    1,    1,    3,
+            1,    3,    3,    1,    0,    1,    1,    3,    3,    4,
+            4,    1,    2,    3,    3,    3,    3,    3,    3,    3,
+            3,    3,    3,    3,    3,    2,    2,    2,    2,    3,
+            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
+            3,    3,    3,    3,    3,    3,    2,    2,    2,    2,
+            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
+            1,    3,    5,    4,    3,    4,    4,    2,    2,    2,
+            2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+            2,    1,    1,    1,    3,    2,    1,    2,   10,   11,
+            3,    3,    2,    4,    4,    3,    4,    4,    4,    4,
+            7,    3,    2,    0,    4,    1,    3,    2,    2,    4,
+            6,    2,    2,    4,    1,    1,    1,    2,    3,    1,
+            1,    1,    1,    1,    1,    3,    3,    4,    4,    0,
+            2,    1,    0,    1,    1,    0,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    3,    2,
+            1,    3,    1,    4,    3,    1,    3,    3,    3,    3,
+            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
+            3,    3,    3,    2,    2,    2,    2,    3,    3,    3,
+            3,    3,    3,    3,    3,    5,    4,    4,    3,    1,
+            3,    1,    1,    3,    3,    0,    2,    0,    1,    3,
+            1,    3,    1,    1,    1,    1,    1,    6,    4,    3,
+            4,    2,    4,    4,    1,    3,    1,    2,    1,    1,
+            4,    1,    3,    6,    4,    4,    4,    4,    1,    4,
+            0,    1,    1,    3,    1,    4,    3,    1,    1,    1,
+            0,    0,    2,    3,    1,    3,    1,    4,    2,    2,
+            2,    1,    2,    1,    1,    4,    3,    3,    3,    6,
+            3,    1,    1,    1
+    );
+
+    protected function reduceRule0() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule1() {
+         $this->semValue = $this->handleNamespaces($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule2() {
+         if (is_array($this->semStack[$this->stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]); } else { $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)]; };
+    }
+
+    protected function reduceRule3() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule4() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule5() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule6() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule7() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule8() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule9() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule10() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule11() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule12() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule13() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule14() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule15() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule16() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule17() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule18() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule19() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule20() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule21() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule22() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule23() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule24() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule25() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule26() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule27() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule28() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule29() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule30() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule31() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule32() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule33() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule34() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule35() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule36() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule37() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule38() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule39() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule40() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule41() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule42() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule43() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule44() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule45() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule46() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule47() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule48() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule49() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule50() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule51() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule52() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule53() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule54() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule55() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule56() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule57() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule58() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule59() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule60() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule61() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule62() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule63() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule64() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule65() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule66() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule67() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule68() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule69() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule70() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule71() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule72() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule73() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule74() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule75() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule76() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule77() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule78() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule79() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule80() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule81() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule82() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule83() {
+         $this->semValue = new Name($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule84() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule85() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule86() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule87() {
+         $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule88() {
+         $this->semValue = new Stmt\Namespace_($this->semStack[$this->stackPos-(3-2)], null, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule89() {
+         $this->semValue = new Stmt\Namespace_($this->semStack[$this->stackPos-(5-2)], $this->semStack[$this->stackPos-(5-4)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule90() {
+         $this->semValue = new Stmt\Namespace_(null, $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule91() {
+         $this->semValue = new Stmt\Use_($this->semStack[$this->stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule92() {
+         $this->semValue = new Stmt\Use_($this->semStack[$this->stackPos-(4-3)], $this->semStack[$this->stackPos-(4-2)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule93() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule94() {
+         $this->semValue = new Stmt\Const_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule95() {
+         $this->semValue = Stmt\Use_::TYPE_FUNCTION;
+    }
+
+    protected function reduceRule96() {
+         $this->semValue = Stmt\Use_::TYPE_CONSTANT;
+    }
+
+    protected function reduceRule97() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], $this->semStack[$this->stackPos-(7-2)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule98() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(8-4)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(8-7)], $this->semStack[$this->stackPos-(8-2)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule99() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule100() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule101() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule102() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule103() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule104() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule105() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule106() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule107() {
+         $this->semValue = new Stmt\UseUse($this->semStack[$this->stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule108() {
+         $this->semValue = new Stmt\UseUse($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule109() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule110() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule111() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)]; $this->semValue->type = Stmt\Use_::TYPE_NORMAL;
+    }
+
+    protected function reduceRule112() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)]; $this->semValue->type = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule113() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule114() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule115() {
+         $this->semValue = new Node\Const_($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule116() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule117() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule118() {
+         $this->semValue = new Node\Const_($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule119() {
+         if (is_array($this->semStack[$this->stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]); } else { $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)]; };
+    }
+
+    protected function reduceRule120() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule121() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule122() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule123() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule124() {
+         throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule125() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule126() {
+         $this->semValue = new Stmt\If_($this->semStack[$this->stackPos-(5-2)], ['stmts' => is_array($this->semStack[$this->stackPos-(5-3)]) ? $this->semStack[$this->stackPos-(5-3)] : array($this->semStack[$this->stackPos-(5-3)]), 'elseifs' => $this->semStack[$this->stackPos-(5-4)], 'else' => $this->semStack[$this->stackPos-(5-5)]], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule127() {
+         $this->semValue = new Stmt\If_($this->semStack[$this->stackPos-(8-2)], ['stmts' => $this->semStack[$this->stackPos-(8-4)], 'elseifs' => $this->semStack[$this->stackPos-(8-5)], 'else' => $this->semStack[$this->stackPos-(8-6)]], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule128() {
+         $this->semValue = new Stmt\While_($this->semStack[$this->stackPos-(3-2)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule129() {
+         $this->semValue = new Stmt\Do_($this->semStack[$this->stackPos-(5-4)], is_array($this->semStack[$this->stackPos-(5-2)]) ? $this->semStack[$this->stackPos-(5-2)] : array($this->semStack[$this->stackPos-(5-2)]), $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule130() {
+         $this->semValue = new Stmt\For_(['init' => $this->semStack[$this->stackPos-(9-3)], 'cond' => $this->semStack[$this->stackPos-(9-5)], 'loop' => $this->semStack[$this->stackPos-(9-7)], 'stmts' => $this->semStack[$this->stackPos-(9-9)]], $this->startAttributeStack[$this->stackPos-(9-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule131() {
+         $this->semValue = new Stmt\Switch_($this->semStack[$this->stackPos-(3-2)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule132() {
+         $this->semValue = new Stmt\Break_(null, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule133() {
+         $this->semValue = new Stmt\Break_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule134() {
+         $this->semValue = new Stmt\Continue_(null, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule135() {
+         $this->semValue = new Stmt\Continue_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule136() {
+         $this->semValue = new Stmt\Return_(null, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule137() {
+         $this->semValue = new Stmt\Return_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule138() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule139() {
+         $this->semValue = new Stmt\Global_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule140() {
+         $this->semValue = new Stmt\Static_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule141() {
+         $this->semValue = new Stmt\Echo_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule142() {
+         $this->semValue = new Stmt\InlineHTML($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule143() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule144() {
+         $this->semValue = new Stmt\Unset_($this->semStack[$this->stackPos-(5-3)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule145() {
+         $this->semValue = new Stmt\Foreach_($this->semStack[$this->stackPos-(7-3)], $this->semStack[$this->stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$this->stackPos-(7-5)][1], 'stmts' => $this->semStack[$this->stackPos-(7-7)]], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule146() {
+         $this->semValue = new Stmt\Foreach_($this->semStack[$this->stackPos-(9-3)], $this->semStack[$this->stackPos-(9-7)][0], ['keyVar' => $this->semStack[$this->stackPos-(9-5)], 'byRef' => $this->semStack[$this->stackPos-(9-7)][1], 'stmts' => $this->semStack[$this->stackPos-(9-9)]], $this->startAttributeStack[$this->stackPos-(9-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule147() {
+         $this->semValue = new Stmt\Declare_($this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-5)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule148() {
+         $this->semValue = new Stmt\TryCatch($this->semStack[$this->stackPos-(6-3)], $this->semStack[$this->stackPos-(6-5)], $this->semStack[$this->stackPos-(6-6)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule149() {
+         $this->semValue = new Stmt\Throw_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule150() {
+         $this->semValue = new Stmt\Goto_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule151() {
+         $this->semValue = new Stmt\Label($this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule152() {
+         $this->semValue = array(); /* means: no statement */
+    }
+
+    protected function reduceRule153() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule154() {
+         $this->semValue = array(); /* means: no statement */
+    }
+
+    protected function reduceRule155() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule156() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule157() {
+         $this->semValue = new Stmt\Catch_($this->semStack[$this->stackPos-(8-3)], substr($this->semStack[$this->stackPos-(8-4)], 1), $this->semStack[$this->stackPos-(8-7)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule158() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule159() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-3)];
+    }
+
+    protected function reduceRule160() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule161() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule162() {
+         $this->semValue = false;
+    }
+
+    protected function reduceRule163() {
+         $this->semValue = true;
+    }
+
+    protected function reduceRule164() {
+         $this->semValue = false;
+    }
+
+    protected function reduceRule165() {
+         $this->semValue = true;
+    }
+
+    protected function reduceRule166() {
+         $this->semValue = new Stmt\Function_($this->semStack[$this->stackPos-(10-3)], ['byRef' => $this->semStack[$this->stackPos-(10-2)], 'params' => $this->semStack[$this->stackPos-(10-5)], 'returnType' => $this->semStack[$this->stackPos-(10-7)], 'stmts' => $this->semStack[$this->stackPos-(10-9)]], $this->startAttributeStack[$this->stackPos-(10-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule167() {
+         $this->semValue = new Stmt\Class_($this->semStack[$this->stackPos-(7-2)], ['type' => $this->semStack[$this->stackPos-(7-1)], 'extends' => $this->semStack[$this->stackPos-(7-3)], 'implements' => $this->semStack[$this->stackPos-(7-4)], 'stmts' => $this->semStack[$this->stackPos-(7-6)]], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule168() {
+         $this->semValue = new Stmt\Interface_($this->semStack[$this->stackPos-(6-2)], ['extends' => $this->semStack[$this->stackPos-(6-3)], 'stmts' => $this->semStack[$this->stackPos-(6-5)]], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule169() {
+         $this->semValue = new Stmt\Trait_($this->semStack[$this->stackPos-(5-2)], $this->semStack[$this->stackPos-(5-4)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule170() {
+         $this->semValue = 0;
+    }
+
+    protected function reduceRule171() {
+         $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT;
+    }
+
+    protected function reduceRule172() {
+         $this->semValue = Stmt\Class_::MODIFIER_FINAL;
+    }
+
+    protected function reduceRule173() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule174() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule175() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule176() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule177() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule178() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule179() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule180() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule181() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule182() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule183() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule184() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule185() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule186() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule187() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule188() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule189() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule190() {
+         $this->semValue = new Stmt\DeclareDeclare($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule191() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule192() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-3)];
+    }
+
+    protected function reduceRule193() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule194() {
+         $this->semValue = $this->semStack[$this->stackPos-(5-3)];
+    }
+
+    protected function reduceRule195() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule196() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule197() {
+         $this->semValue = new Stmt\Case_($this->semStack[$this->stackPos-(4-2)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule198() {
+         $this->semValue = new Stmt\Case_(null, $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule199() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule200() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule201() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule202() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule203() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule204() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule205() {
+         $this->semValue = new Stmt\ElseIf_($this->semStack[$this->stackPos-(3-2)], is_array($this->semStack[$this->stackPos-(3-3)]) ? $this->semStack[$this->stackPos-(3-3)] : array($this->semStack[$this->stackPos-(3-3)]), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule206() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule207() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule208() {
+         $this->semValue = new Stmt\ElseIf_($this->semStack[$this->stackPos-(4-2)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule209() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule210() {
+         $this->semValue = new Stmt\Else_(is_array($this->semStack[$this->stackPos-(2-2)]) ? $this->semStack[$this->stackPos-(2-2)] : array($this->semStack[$this->stackPos-(2-2)]), $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule211() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule212() {
+         $this->semValue = new Stmt\Else_($this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule213() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)], false);
+    }
+
+    protected function reduceRule214() {
+         $this->semValue = array($this->semStack[$this->stackPos-(2-2)], true);
+    }
+
+    protected function reduceRule215() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)], false);
+    }
+
+    protected function reduceRule216() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule217() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule218() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule219() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule220() {
+         $this->semValue = new Node\Param(substr($this->semStack[$this->stackPos-(4-4)], 1), null, $this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-2)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule221() {
+         $this->semValue = new Node\Param(substr($this->semStack[$this->stackPos-(6-4)], 1), $this->semStack[$this->stackPos-(6-6)], $this->semStack[$this->stackPos-(6-1)], $this->semStack[$this->stackPos-(6-2)], $this->semStack[$this->stackPos-(6-3)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule222() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule223() {
+         $this->semValue = 'array';
+    }
+
+    protected function reduceRule224() {
+         $this->semValue = 'callable';
+    }
+
+    protected function reduceRule225() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule226() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule227() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule228() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule229() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule230() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule231() {
+         $this->semValue = array(new Node\Arg($this->semStack[$this->stackPos-(3-2)], false, false, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes));
+    }
+
+    protected function reduceRule232() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule233() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule234() {
+         $this->semValue = new Node\Arg($this->semStack[$this->stackPos-(1-1)], false, false, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule235() {
+         $this->semValue = new Node\Arg($this->semStack[$this->stackPos-(2-2)], true, false, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule236() {
+         $this->semValue = new Node\Arg($this->semStack[$this->stackPos-(2-2)], false, true, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule237() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule238() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule239() {
+         $this->semValue = new Expr\Variable(substr($this->semStack[$this->stackPos-(1-1)], 1), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule240() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule241() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule242() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule243() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule244() {
+         $this->semValue = new Stmt\StaticVar(substr($this->semStack[$this->stackPos-(1-1)], 1), null, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule245() {
+         $this->semValue = new Stmt\StaticVar(substr($this->semStack[$this->stackPos-(3-1)], 1), $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule246() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule247() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule248() {
+         $this->semValue = new Stmt\Property($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule249() {
+         $this->semValue = new Stmt\ClassConst($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule250() {
+         $this->semValue = new Stmt\ClassMethod($this->semStack[$this->stackPos-(9-4)], ['type' => $this->semStack[$this->stackPos-(9-1)], 'byRef' => $this->semStack[$this->stackPos-(9-3)], 'params' => $this->semStack[$this->stackPos-(9-6)], 'returnType' => $this->semStack[$this->stackPos-(9-8)], 'stmts' => $this->semStack[$this->stackPos-(9-9)]], $this->startAttributeStack[$this->stackPos-(9-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule251() {
+         $this->semValue = new Stmt\TraitUse($this->semStack[$this->stackPos-(3-2)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule252() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule253() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule254() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule255() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule256() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule257() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(5-1)][0], $this->semStack[$this->stackPos-(5-1)][1], $this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-4)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule258() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], $this->semStack[$this->stackPos-(4-3)], null, $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule259() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], null, $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule260() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], null, $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule261() {
+         $this->semValue = array($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)]);
+    }
+
+    protected function reduceRule262() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule263() {
+         $this->semValue = array(null, $this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule264() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule265() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule266() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule267() {
+         $this->semValue = 0;
+    }
+
+    protected function reduceRule268() {
+         $this->semValue = 0;
+    }
+
+    protected function reduceRule269() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule270() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule271() {
+         Stmt\Class_::verifyModifier($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]); $this->semValue = $this->semStack[$this->stackPos-(2-1)] | $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule272() {
+         $this->semValue = Stmt\Class_::MODIFIER_PUBLIC;
+    }
+
+    protected function reduceRule273() {
+         $this->semValue = Stmt\Class_::MODIFIER_PROTECTED;
+    }
+
+    protected function reduceRule274() {
+         $this->semValue = Stmt\Class_::MODIFIER_PRIVATE;
+    }
+
+    protected function reduceRule275() {
+         $this->semValue = Stmt\Class_::MODIFIER_STATIC;
+    }
+
+    protected function reduceRule276() {
+         $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT;
+    }
+
+    protected function reduceRule277() {
+         $this->semValue = Stmt\Class_::MODIFIER_FINAL;
+    }
+
+    protected function reduceRule278() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule279() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule280() {
+         $this->semValue = new Stmt\PropertyProperty(substr($this->semStack[$this->stackPos-(1-1)], 1), null, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule281() {
+         $this->semValue = new Stmt\PropertyProperty(substr($this->semStack[$this->stackPos-(3-1)], 1), $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule282() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule283() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule284() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule285() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule286() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule287() {
+         $this->semValue = new Expr\Assign($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule288() {
+         $this->semValue = new Expr\Assign($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule289() {
+         $this->semValue = new Expr\AssignRef($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule290() {
+         $this->semValue = new Expr\AssignRef($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule291() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule292() {
+         $this->semValue = new Expr\Clone_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule293() {
+         $this->semValue = new Expr\AssignOp\Plus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule294() {
+         $this->semValue = new Expr\AssignOp\Minus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule295() {
+         $this->semValue = new Expr\AssignOp\Mul($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule296() {
+         $this->semValue = new Expr\AssignOp\Div($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule297() {
+         $this->semValue = new Expr\AssignOp\Concat($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule298() {
+         $this->semValue = new Expr\AssignOp\Mod($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule299() {
+         $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule300() {
+         $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule301() {
+         $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule302() {
+         $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule303() {
+         $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule304() {
+         $this->semValue = new Expr\AssignOp\Pow($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule305() {
+         $this->semValue = new Expr\PostInc($this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule306() {
+         $this->semValue = new Expr\PreInc($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule307() {
+         $this->semValue = new Expr\PostDec($this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule308() {
+         $this->semValue = new Expr\PreDec($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule309() {
+         $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule310() {
+         $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule311() {
+         $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule312() {
+         $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule313() {
+         $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule314() {
+         $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule315() {
+         $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule316() {
+         $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule317() {
+         $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule318() {
+         $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule319() {
+         $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule320() {
+         $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule321() {
+         $this->semValue = new Expr\BinaryOp\Div($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule322() {
+         $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule323() {
+         $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule324() {
+         $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule325() {
+         $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule326() {
+         $this->semValue = new Expr\UnaryPlus($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule327() {
+         $this->semValue = new Expr\UnaryMinus($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule328() {
+         $this->semValue = new Expr\BooleanNot($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule329() {
+         $this->semValue = new Expr\BitwiseNot($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule330() {
+         $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule331() {
+         $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule332() {
+         $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule333() {
+         $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule334() {
+         $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule335() {
+         $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule336() {
+         $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule337() {
+         $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule338() {
+         $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule339() {
+         $this->semValue = new Expr\Instanceof_($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule340() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule341() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule342() {
+         $this->semValue = new Expr\Ternary($this->semStack[$this->stackPos-(5-1)], $this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-5)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule343() {
+         $this->semValue = new Expr\Ternary($this->semStack[$this->stackPos-(4-1)], null, $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule344() {
+         $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule345() {
+         $this->semValue = new Expr\Isset_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule346() {
+         $this->semValue = new Expr\Empty_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule347() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule348() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule349() {
+         $this->semValue = new Expr\Eval_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule350() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule351() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule352() {
+         $this->semValue = new Expr\Cast\Int_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule353() {
+         $this->semValue = new Expr\Cast\Double($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule354() {
+         $this->semValue = new Expr\Cast\String_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule355() {
+         $this->semValue = new Expr\Cast\Array_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule356() {
+         $this->semValue = new Expr\Cast\Object_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule357() {
+         $this->semValue = new Expr\Cast\Bool_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule358() {
+         $this->semValue = new Expr\Cast\Unset_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule359() {
+         $this->semValue = new Expr\Exit_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule360() {
+         $this->semValue = new Expr\ErrorSuppress($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule361() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule362() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule363() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule364() {
+         $this->semValue = new Expr\ShellExec($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule365() {
+         $this->semValue = new Expr\Print_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule366() {
+         $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule367() {
+         $this->semValue = new Expr\YieldFrom($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule368() {
+         $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$this->stackPos-(10-2)], 'params' => $this->semStack[$this->stackPos-(10-4)], 'uses' => $this->semStack[$this->stackPos-(10-6)], 'returnType' => $this->semStack[$this->stackPos-(10-7)], 'stmts' => $this->semStack[$this->stackPos-(10-9)]], $this->startAttributeStack[$this->stackPos-(10-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule369() {
+         $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$this->stackPos-(11-3)], 'params' => $this->semStack[$this->stackPos-(11-5)], 'uses' => $this->semStack[$this->stackPos-(11-7)], 'returnType' => $this->semStack[$this->stackPos-(11-8)], 'stmts' => $this->semStack[$this->stackPos-(11-10)]], $this->startAttributeStack[$this->stackPos-(11-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule370() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule371() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule372() {
+         $this->semValue = new Expr\Yield_($this->semStack[$this->stackPos-(2-2)], null, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule373() {
+         $this->semValue = new Expr\Yield_($this->semStack[$this->stackPos-(4-4)], $this->semStack[$this->stackPos-(4-2)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule374() {
+         $this->semValue = new Expr\Array_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule375() {
+         $this->semValue = new Expr\Array_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule376() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule377() {
+         $this->semValue = new Expr\ArrayDimFetch(new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(4-1)], false), $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule378() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule379() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule380() {
+         $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$this->stackPos-(7-3)], 'implements' => $this->semStack[$this->stackPos-(7-4)], 'stmts' => $this->semStack[$this->stackPos-(7-6)]], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-2)]);
+    }
+
+    protected function reduceRule381() {
+         $this->semValue = new Expr\New_($this->semStack[$this->stackPos-(3-2)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule382() {
+         list($class, $ctorArgs) = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule383() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule384() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-3)];
+    }
+
+    protected function reduceRule385() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule386() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule387() {
+         $this->semValue = new Expr\ClosureUse(substr($this->semStack[$this->stackPos-(2-2)], 1), $this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule388() {
+         $this->semValue = new Expr\FuncCall($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule389() {
+         $this->semValue = new Expr\StaticCall($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule390() {
+         $this->semValue = new Expr\StaticCall($this->semStack[$this->stackPos-(6-1)], $this->semStack[$this->stackPos-(6-4)], $this->semStack[$this->stackPos-(6-6)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule391() {
+
+            if ($this->semStack[$this->stackPos-(2-1)] instanceof Node\Expr\StaticPropertyFetch) {
+                $this->semValue = new Expr\StaticCall($this->semStack[$this->stackPos-(2-1)]->class, new Expr\Variable($this->semStack[$this->stackPos-(2-1)]->name, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+            } elseif ($this->semStack[$this->stackPos-(2-1)] instanceof Node\Expr\ArrayDimFetch) {
+                $tmp = $this->semStack[$this->stackPos-(2-1)];
+                while ($tmp->var instanceof Node\Expr\ArrayDimFetch) {
+                    $tmp = $tmp->var;
+                }
+
+                $this->semValue = new Expr\StaticCall($tmp->var->class, $this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+                $tmp->var = new Expr\Variable($tmp->var->name, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+            } else {
+                throw new \Exception;
+            }
+
+    }
+
+    protected function reduceRule392() {
+         $this->semValue = new Expr\FuncCall($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule393() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule394() {
+         $this->semValue = new Name($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule395() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule396() {
+         $this->semValue = new Name($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule397() {
+         $this->semValue = new Name\FullyQualified($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule398() {
+         $this->semValue = new Name\Relative($this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule399() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule400() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule401() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule402() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule403() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule404() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule405() {
+         $this->semValue = new Expr\PropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule406() {
+         $this->semValue = new Expr\PropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule407() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule408() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule409() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule410() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule411() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule412() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule413() {
+         $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$this->stackPos-(1-1)], '`', false), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes));
+    }
+
+    protected function reduceRule414() {
+         foreach ($this->semStack[$this->stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', false); } }; $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule415() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule416() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule417() {
+         $this->semValue = new Scalar\LNumber(Scalar\LNumber::parse($this->semStack[$this->stackPos-(1-1)]), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule418() {
+         $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$this->stackPos-(1-1)]), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule419() {
+         $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(1-1)], false), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule420() {
+         $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule421() {
+         $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule422() {
+         $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule423() {
+         $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule424() {
+         $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule425() {
+         $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule426() {
+         $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule427() {
+         $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule428() {
+         $this->semValue = new Scalar\String_(Scalar\String_::parseDocString($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)], false), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule429() {
+         $this->semValue = new Scalar\String_('', $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule430() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule431() {
+         $this->semValue = new Expr\ClassConstFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule432() {
+         $this->semValue = new Expr\ConstFetch($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule433() {
+         $this->semValue = new Expr\Array_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule434() {
+         $this->semValue = new Expr\Array_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule435() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule436() {
+         $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule437() {
+         $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule438() {
+         $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule439() {
+         $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule440() {
+         $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule441() {
+         $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule442() {
+         $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule443() {
+         $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule444() {
+         $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule445() {
+         $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule446() {
+         $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule447() {
+         $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule448() {
+         $this->semValue = new Expr\BinaryOp\Div($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule449() {
+         $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule450() {
+         $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule451() {
+         $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule452() {
+         $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule453() {
+         $this->semValue = new Expr\UnaryPlus($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule454() {
+         $this->semValue = new Expr\UnaryMinus($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule455() {
+         $this->semValue = new Expr\BooleanNot($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule456() {
+         $this->semValue = new Expr\BitwiseNot($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule457() {
+         $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule458() {
+         $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule459() {
+         $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule460() {
+         $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule461() {
+         $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule462() {
+         $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule463() {
+         $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule464() {
+         $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule465() {
+         $this->semValue = new Expr\Ternary($this->semStack[$this->stackPos-(5-1)], $this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-5)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule466() {
+         $this->semValue = new Expr\Ternary($this->semStack[$this->stackPos-(4-1)], null, $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule467() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule468() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule469() {
+         $this->semValue = new Expr\ConstFetch($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule470() {
+         $this->semValue = new Expr\ClassConstFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule471() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule472() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule473() {
+         foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', false); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule474() {
+         foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, false); } } $s->value = preg_replace('~(\r\n|\n|\r)\z~', '', $s->value); if ('' === $s->value) array_pop($this->semStack[$this->stackPos-(3-2)]);; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule475() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule476() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule477() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule478() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule479() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule480() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule481() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(3-3)], $this->semStack[$this->stackPos-(3-1)], false, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule482() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(1-1)], null, false, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule483() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule484() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule485() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule486() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule487() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(6-2)], $this->semStack[$this->stackPos-(6-5)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule488() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule489() {
+         $this->semValue = new Expr\PropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule490() {
+         $this->semValue = new Expr\MethodCall($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule491() {
+         $this->semValue = new Expr\FuncCall($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule492() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule493() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule494() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule495() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule496() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule497() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule498() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule499() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule500() {
+         $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule501() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule502() {
+         $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$this->stackPos-(3-1)], substr($this->semStack[$this->stackPos-(3-3)], 1), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule503() {
+         $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$this->stackPos-(6-1)], $this->semStack[$this->stackPos-(6-5)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule504() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule505() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule506() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule507() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule508() {
+         $this->semValue = new Expr\Variable(substr($this->semStack[$this->stackPos-(1-1)], 1), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule509() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule510() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule511() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule512() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule513() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule514() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule515() {
+         $this->semValue = new Expr\List_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule516() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule517() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule518() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule519() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule520() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule521() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule522() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule523() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule524() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule525() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(3-3)], $this->semStack[$this->stackPos-(3-1)], false, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule526() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(1-1)], null, false, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule527() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(4-4)], $this->semStack[$this->stackPos-(4-1)], true, $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule528() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(2-2)], null, true, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule529() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule530() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule531() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule532() {
+         $this->semValue = array($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]);
+    }
+
+    protected function reduceRule533() {
+         $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule534() {
+         $this->semValue = new Expr\Variable(substr($this->semStack[$this->stackPos-(1-1)], 1), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule535() {
+         $this->semValue = new Expr\ArrayDimFetch(new Expr\Variable(substr($this->semStack[$this->stackPos-(4-1)], 1), $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule536() {
+         $this->semValue = new Expr\PropertyFetch(new Expr\Variable(substr($this->semStack[$this->stackPos-(3-1)], 1), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule537() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule538() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule539() {
+         $this->semValue = new Expr\ArrayDimFetch(new Expr\Variable($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(6-4)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule540() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule541() {
+         $this->semValue = new Scalar\String_($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule542() {
+         $this->semValue = new Scalar\String_($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule543() {
+         $this->semValue = new Expr\Variable(substr($this->semStack[$this->stackPos-(1-1)], 1), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Php7.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Php7.php
new file mode 100644
index 00000000000..20eab15c260
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Php7.php
@@ -0,0 +1,2719 @@
+'",
+        "T_IS_GREATER_OR_EQUAL",
+        "T_SL",
+        "T_SR",
+        "'+'",
+        "'-'",
+        "'.'",
+        "'*'",
+        "'/'",
+        "'%'",
+        "'!'",
+        "T_INSTANCEOF",
+        "'~'",
+        "T_INC",
+        "T_DEC",
+        "T_INT_CAST",
+        "T_DOUBLE_CAST",
+        "T_STRING_CAST",
+        "T_ARRAY_CAST",
+        "T_OBJECT_CAST",
+        "T_BOOL_CAST",
+        "T_UNSET_CAST",
+        "'@'",
+        "T_POW",
+        "'['",
+        "T_NEW",
+        "T_CLONE",
+        "T_EXIT",
+        "T_IF",
+        "T_ELSEIF",
+        "T_ELSE",
+        "T_ENDIF",
+        "T_LNUMBER",
+        "T_DNUMBER",
+        "T_STRING",
+        "T_STRING_VARNAME",
+        "T_VARIABLE",
+        "T_NUM_STRING",
+        "T_INLINE_HTML",
+        "T_ENCAPSED_AND_WHITESPACE",
+        "T_CONSTANT_ENCAPSED_STRING",
+        "T_ECHO",
+        "T_DO",
+        "T_WHILE",
+        "T_ENDWHILE",
+        "T_FOR",
+        "T_ENDFOR",
+        "T_FOREACH",
+        "T_ENDFOREACH",
+        "T_DECLARE",
+        "T_ENDDECLARE",
+        "T_AS",
+        "T_SWITCH",
+        "T_ENDSWITCH",
+        "T_CASE",
+        "T_DEFAULT",
+        "T_BREAK",
+        "T_CONTINUE",
+        "T_GOTO",
+        "T_FUNCTION",
+        "T_CONST",
+        "T_RETURN",
+        "T_TRY",
+        "T_CATCH",
+        "T_FINALLY",
+        "T_THROW",
+        "T_USE",
+        "T_INSTEADOF",
+        "T_GLOBAL",
+        "T_STATIC",
+        "T_ABSTRACT",
+        "T_FINAL",
+        "T_PRIVATE",
+        "T_PROTECTED",
+        "T_PUBLIC",
+        "T_VAR",
+        "T_UNSET",
+        "T_ISSET",
+        "T_EMPTY",
+        "T_HALT_COMPILER",
+        "T_CLASS",
+        "T_TRAIT",
+        "T_INTERFACE",
+        "T_EXTENDS",
+        "T_IMPLEMENTS",
+        "T_OBJECT_OPERATOR",
+        "T_LIST",
+        "T_ARRAY",
+        "T_CALLABLE",
+        "T_CLASS_C",
+        "T_TRAIT_C",
+        "T_METHOD_C",
+        "T_FUNC_C",
+        "T_LINE",
+        "T_FILE",
+        "T_START_HEREDOC",
+        "T_END_HEREDOC",
+        "T_DOLLAR_OPEN_CURLY_BRACES",
+        "T_CURLY_OPEN",
+        "T_PAAMAYIM_NEKUDOTAYIM",
+        "T_NAMESPACE",
+        "T_NS_C",
+        "T_DIR",
+        "T_NS_SEPARATOR",
+        "T_ELLIPSIS",
+        "';'",
+        "'{'",
+        "'}'",
+        "'('",
+        "')'",
+        "'`'",
+        "']'",
+        "'\"'",
+        "'$'"
+    );
+
+    protected $tokenToSymbol = array(
+            0,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,   53,  155,  157,  156,   52,   35,  157,
+          151,  152,   50,   47,    7,   48,   49,   51,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,   29,  148,
+           41,   15,   43,   28,   65,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,   67,  157,  154,   34,  157,  153,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  149,   33,  150,   55,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,  157,  157,  157,  157,
+          157,  157,  157,  157,  157,  157,    1,    2,    3,    4,
+            5,    6,    8,    9,   10,   11,   12,   13,   14,   16,
+           17,   18,   19,   20,   21,   22,   23,   24,   25,   26,
+           27,   30,   31,   32,   36,   37,   38,   39,   40,   42,
+           44,   45,   46,   54,   56,   57,   58,   59,   60,   61,
+           62,   63,   64,   66,   68,   69,   70,   71,   72,   73,
+           74,   75,   76,   77,   78,   79,   80,   81,  157,  157,
+           82,   83,   84,   85,   86,   87,   88,   89,   90,   91,
+           92,   93,   94,   95,   96,   97,   98,   99,  100,  101,
+          102,  103,  104,  105,  106,  107,  108,  109,  110,  111,
+          112,  113,  114,  115,  116,  117,  118,  119,  120,  121,
+          122,  123,  124,  125,  126,  127,  128,  129,  130,  131,
+          132,  133,  134,  135,  136,  137,  157,  157,  157,  157,
+          157,  157,  138,  139,  140,  141,  142,  143,  144,  145,
+          146,  147
+    );
+
+    protected $action = array(
+          705,   58,   59,  391,   60,   61,-32766,-32766,-32766,-32766,
+           62,   63, 1028,   64, 1030, 1029,-32766,-32766,-32766,-32766,
+        -32766,-32766,-32766,-32766,-32766,-32766,-32766,  129,-32766,-32766,
+        -32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767,-32767,-32766,
+            0,-32766,-32766,-32766,-32766,-32766,   65,   66,   55,  108,
+          109,  110,   67,  287,   68,  233,  234,   69,   70,   71,
+           72,   73,   74,   75,   76,  355,   40,  247,   77,  360,
+          392,  283,-32766,  353,  951,  952,  393,  240,  986,  379,
+          695,  297,  950,   50,   35,  394,-32766,  395,  539,  396,
+        -32766,  397,  388,-32766,  398,-32766,-32766,-32766,   51,   52,
+          399,  361,-32766,   53,  400,  140,   42,   78,  999,  268,
+          298,  299,  401,  402,  463,-32766,  131,  752,  403,  404,
+          405,  681,  723,  406,  407,  776,  777,  986,  408,  409,
+          128,  956,  957,  958,  959,  953,  954,  255,  418,  526,
+          276,  362,  418,  960,  955,  362,  254,  707,  529,-32766,
+           79,   25,  265,  371,  266,  285,  561,  562,  563,  564,
+          565,  364,  566,  567,  568,  604,  605,-32766,-32766,-32766,
+        -32766,  408,-32766,-32766,  825,  826,  827,  824,  823,  822,
+          137,-32766,-32766,-32766,-32766,  652,  653,-32766, 1025,-32766,
+        -32766,-32766,-32766,-32766,-32766,   46,-32766,-32766,-32766,  380,
+           24,  111,  112,  113,  285,  253,  279,  739,  569,  825,
+          826,  827,  824,  823,  822,  817,-32766,  114,-32766,-32766,
+        -32766,-32766,  570,  571,  572,  573,  574,  575,  576,  348,
+          239,  636,  418,  798,  317,  362,  753, 1020,  577,  578,
+          579,  580,  581,  582,  583,  584,  585,  586,  587,  607,
+          608,  609,  610,  611,  599,  600,  601,  602,  603,  588,
+          589,  590,  591,  592,  593,  594,  630,  631,  632,  633,
+          634,  635,  595,  596,  597,  598,  628,  619,  617,  618,
+          614,  615,-32766,  606,  612,  613,  620,  621,  623,  622,
+          624,  625,  799, -415,  651,   43,  236,  616,  627,  626,
+          144,   88,   89,   90,-32767,-32767,-32767,-32767,  106,  107,
+          108,  109,  110, -204, -204, -204,  237,  146, -193, -193,
+         -193,   44,  444,   91,   92,   93,   94,   95,   96,   97,
+           98,   99,  100,  101,  102,  103,  104,  105,  106,  107,
+          108,  109,  110,  111,  112,  113,  143,  253, -194, -194,
+         -194,  354,-32766, -448, -415,  372,-32766,  264,  638,  114,
+           39,  704,-32766, -458,  359,-32766,-32766,-32766, -415,-32766,
+          644,-32766, -193,-32766,  227, -415,-32766, -418,  139,  964,
+        -32766,-32766,-32766,  364,  127,-32766,-32766,  300,  132,-32766,
+          237,  281,-32766,  417,-32766,-32766,  368,   -1,  647,  531,
+        -32766, -416, -194,-32766,-32766,-32766,-32766,  133,   40, -202,
+         -202, -202, -457,  229,  141,-32766,-32766,-32766,  638, -230,
+          986,  716,  517, -157,  950,  256,-32766,  550,  135,-32766,
+        -32766,-32766,  638,  782,  114,-32766,-32766,-32766,-32766,-32766,
+        -32766,-32766,  354,-32766,  555,-32766,  377,-32766,  264,  253,
+        -32766, -414, -208,  930,-32766,-32766,-32766,  364, -156,-32766,
+        -32766,  644, -416,-32766,  514,  238,-32766,  417,-32766,-32766,
+           56,  409,-32766,-32766,-32766,  452, -416,-32766,-32766,-32766,
+        -32766,  247,  281, -416,  418, -419,-32766,  362,  518,  646,
+           57,  362,   81,  506,  507,  803,  693,  285, -448,  639,
+          354,-32766,  135,-32766,-32766,  783,  264,  638, -458,  923,
+         -458,-32766, -414, -209,-32766,-32766,-32766,  456,-32766,  644,
+        -32766,  551,-32766,  776,  777,-32766, -414,  538,  692,-32766,
+        -32766,-32766,  364, -414,-32766,-32766,  418, -259,-32766,  362,
+          281,-32766,  417,-32766,-32766, -213,  496,   36,  986,-32766,
+          638,  269,-32766,-32766,-32766,-32766,-32766, -457, 1002, -457,
+        -32766,-32766,  638,  327, -230,  306,-32766,-32766, -157,-32766,
+        -32766,-32766,  130,-32766,  443,-32766,-32766,-32766,-32766,-32766,
+        -32766,  142,  308,  438,-32766,-32766,-32766,  364,  134,-32766,
+        -32766,   34,  536,-32766,  364,  437,-32766,  417,-32766,-32766,
+          364,  652,  653, -156,-32766,-32766,-32766,-32766,-32766,-32766,
+        -32766,-32766,-32766,  665,-32766,-32766,-32766,-32766,  136,  369,
+        -32766,-32766,-32766,-32766, 1023,  285,  491,  492,  645,  533,
+        -32766,  802,  546,-32766,-32766,-32766,  638,  814,  556,  455,
+        -32766,  367,-32766,-32766,-32766,-32766,  926,-32766,  516,-32766,
+          494,-32766,  505,  442,-32766,  497,  515,  447,-32766,-32766,
+        -32766,  364,  509,-32766,-32766,  364,  525,-32766,  502,  235,
+        -32766,  417,-32766,-32766,  501,  724,-32766,-32766,-32766,  -79,
+          458,-32766,-32766,-32766,-32766,-32766,-32766,-32766,-32766,   28,
+        -32766,-32766,-32766,  226,  366,  386,  489,  278,  280,  725,
+          963,  333,  350,  349,  282,-32766,  277,-32766,-32766,-32766,
+        -32766,  638,-32766,-32766,-32766,-32766,    0,    0,-32766,-32766,
+        -32766,  966,-32766,  408,-32766,   21,-32766,  650,    0,-32766,
+          449, -374,   27,-32766,-32766,-32766,  364,  368,-32766,-32766,
+          364,    0,-32766,  230,  305,-32766,  417,-32766,-32766,  337,
+          320,-32766,-32766,-32766,  332,  689,-32766,-32766,-32766,-32766,
+        -32766,-32766,-32766,-32766,-32766,  718,  532,  690,  542,   49,
+           48,  807,  694,  703,  691,  697,  809,  810,  686,-32766,
+          755,  740,-32766,-32766,  808,  638,   87,  649,  648,-32766,
+          746,  747,-32766,-32766,-32766,  684,-32766,  737,-32766,  735,
+        -32766,  702,  543,-32766,  806,  696, -417,-32766,-32766,-32766,
+          364,  535,-32766,-32766,  540,  541,-32766,  545,  548,-32766,
+          417,-32766,-32766,  549,  553,  554,  274,-32766,  275,  544,
+        -32766,-32766,-32766,-32766,-32766,  346,  347,  530,  911,  987,
+          638,  980,  992,  997,-32766, 1000, 1024,-32766,-32766,-32766,
+          654,-32766, 1027,-32766, 1026,-32766,-32766,-32766,-32766,  657,
+          656,  722,-32766,-32766,-32766,  364,  815,-32766,-32766,  710,
+          918,-32766,  917,  719,-32766,  417,-32766,-32766,  712,  745,
+          744,  919,-32766,  720,  721,-32766,-32766,-32766,-32766,   45,
+          115,  116,  117,  118,  119,  120,  121,  122,  123,  124,
+          125,  126,  364,  682,  534,  655,  949, -418, -419, -439,
+          357,-32766,-32766,-32766,-32766,  352,  284,  252, -441,   38,
+          251,  250,-32766,-32766,-32766,-32766,  249,  232,  231,  228,
+          855,  857,  147,  145,  138,   86,   85,   84,   83,   82,
+           80, -414,   54,   47,   41,  965,  894,  476, -210, -209,
+           29,   33,    0,   37,  260,  307,  471,  487,  524,  898,
+          895,  948,  940,  522,  389,  385,  383,  381,   32,   31,
+           30,    0, -385,    0,  495, 1022,  991,  979,  978,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0, -414,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0, -414,    0,    0,    0,
+            0,    0,    0, -414
+    );
+
+    protected $actionCheck = array(
+            1,    2,    3,    4,    5,    6,    8,    9,   10,   67,
+           11,   12,   77,   14,   79,   80,    8,    9,   10,    8,
+            9,   10,    8,    9,   10,   83,   28,   13,   30,   31,
+           32,   33,   34,   35,   36,   37,   38,   39,   40,   28,
+            0,   30,   31,   32,   33,   34,   47,   48,   67,   47,
+           48,   49,   53,   67,   55,   56,   57,   58,   59,   60,
+           61,   62,   63,   64,   65,  123,   67,   68,   69,   70,
+           71,    7,  130,    7,   75,   76,   77,   35,   79,   29,
+           81,    7,   83,   84,   85,   86,   67,   88,   29,   90,
+            8,   92,    7,  151,   95,    8,    9,   10,   99,  100,
+          101,  102,   83,  104,  105,   67,    7,  108,   77,  128,
+          111,  112,  113,  114,  128,   28,    7,   29,  119,  120,
+          121,  122,  123,  124,  125,  130,  131,   79,  129,  130,
+          149,  132,  133,  134,  135,  136,  137,  138,  143,   77,
+            7,  146,  143,  144,  145,  146,    7,  148,  149,  130,
+          151,    7,  153,    7,  155,  156,    2,    3,    4,    5,
+            6,  102,    8,    9,   10,   11,   12,    8,    9,   10,
+          151,  129,  113,  114,  112,  113,  114,  115,  116,  117,
+          149,  122,  123,  124,  125,  102,  103,   28,  150,   30,
+           31,   32,   33,   34,   35,   13,    8,    9,   10,  149,
+          103,   50,   51,   52,  156,   54,  109,  148,   54,  112,
+          113,  114,  115,  116,  117,  118,   28,   66,   30,   31,
+           32,   33,   68,   69,   70,   71,   72,   73,   74,  146,
+            7,   77,  143,  148,   79,  146,  148,   82,   84,   85,
+           86,   87,   88,   89,   90,   91,   92,   93,   94,   95,
+           96,   97,   98,   99,  100,  101,  102,  103,  104,  105,
+          106,  107,  108,  109,  110,  111,  112,  113,  114,  115,
+          116,  117,  118,  119,  120,  121,  122,  123,  124,  125,
+          126,  127,    1,  129,  130,  131,  132,  133,  134,  135,
+          136,  137,  148,   67,  148,  140,  141,  143,  144,  145,
+           15,    8,    9,   10,   41,   42,   43,   44,   45,   46,
+           47,   48,   49,   72,   73,   74,   35,   15,   96,   97,
+           98,   28,   82,   30,   31,   32,   33,   34,   35,   36,
+           37,   38,   39,   40,   41,   42,   43,   44,   45,   46,
+           47,   48,   49,   50,   51,   52,   15,   54,   96,   97,
+           98,  103,   71,    7,  128,    7,    1,  109,   77,   66,
+            7,   29,   81,    7,    7,   84,   85,   86,  142,   88,
+          122,   90,  150,   92,   13,  149,   95,  151,   15,  139,
+           99,  100,  101,  102,   15,  104,  105,    7,   15,  108,
+           35,  143,  111,  112,  113,  114,  146,    0,  150,  149,
+          119,   67,  150,  122,  123,  124,  125,   15,   67,   72,
+           73,   74,    7,    7,   29,    8,    9,   10,   77,    7,
+           79,   35,   77,    7,   83,   29,   71,   29,  147,  148,
+          149,    1,   77,  152,   66,   28,   81,   30,   31,   84,
+           85,   86,  103,   88,   29,   90,    7,   92,  109,   54,
+           95,   67,   74,  112,   99,  100,  101,  102,    7,  104,
+          105,  122,  128,  108,   74,   35,  111,  112,  113,  114,
+           67,  130,    8,    9,  119,   86,  142,  122,  123,  124,
+          125,   68,  143,  149,  143,  151,    1,  146,  143,  150,
+           67,  146,  151,   72,   73,  150,  148,  156,  152,   77,
+          103,   71,  147,  148,  149,  152,  109,   77,  152,  152,
+          154,   81,  128,  152,   84,   85,   86,   77,   88,  122,
+           90,   29,   92,  130,  131,   95,  142,   29,  148,   99,
+          100,  101,  102,  149,  104,  105,  143,   79,  108,  146,
+          143,  111,  112,  113,  114,  152,   72,   73,   79,  119,
+           77,  128,  122,  123,  124,  125,   71,  152,  152,  154,
+            1,  103,   77,   78,  152,  142,   81,  109,  152,   84,
+           85,   86,  149,   88,   77,   90,  118,   92,  148,  149,
+           95,   97,   98,   77,   99,  100,  101,  102,   29,  104,
+          105,  152,   29,  108,  102,   77,  111,  112,  113,  114,
+          102,  102,  103,  152,  119,  113,  114,  122,  123,  124,
+          125,  113,  114,   77,  122,  123,  124,  125,  149,   77,
+          122,  123,  124,  125,   77,  156,  106,  107,  148,  149,
+           71,  148,  149,  148,  149,    1,   77,  148,  149,   77,
+           81,   77,   82,   84,   85,   86,   79,   88,   79,   90,
+           79,   92,   79,   79,   95,   87,   91,   82,   99,  100,
+          101,  102,   96,  104,  105,  102,   89,  108,   96,   35,
+          111,  112,  113,  114,   93,  123,  113,  114,  119,   94,
+           94,  122,  123,  124,  125,  122,  123,  124,  125,   94,
+            8,    9,   10,   94,  102,  102,  109,  127,  110,  123,
+          139,  146,  146,  146,  126,   71,  126,  148,  149,    1,
+           28,   77,   30,   31,   32,   81,   -1,   -1,   84,   85,
+           86,  139,   88,  129,   90,  142,   92,  148,   -1,   95,
+          146,  142,  142,   99,  100,  101,  102,  146,  104,  105,
+          102,   -1,  108,   35,  142,  111,  112,  113,  114,  146,
+          146,  113,  114,  119,  146,  148,  122,  123,  124,  125,
+          122,  123,  124,  125,    1,  147,  149,  148,  148,  148,
+          148,  148,  148,  148,  148,  148,  148,  148,  148,   71,
+          148,  148,  148,  149,  148,   77,  149,  148,  148,   81,
+          148,  148,   84,   85,   86,  148,   88,  148,   90,  148,
+           92,  148,  148,   95,  148,  148,  151,   99,  100,  101,
+          102,  149,  104,  105,  149,  149,  108,  149,  149,  111,
+          112,  113,  114,  149,  149,  149,  149,  119,  149,   29,
+          122,  123,  124,  125,   71,  149,  149,  149,  153,  150,
+           77,  150,  150,  150,   81,  150,  150,   84,   85,   86,
+          150,   88,  150,   90,  150,   92,  148,  149,   95,  150,
+          150,  150,   99,  100,  101,  102,  150,  104,  105,  150,
+          150,  108,  150,  150,  111,  112,  113,  114,  150,  150,
+          150,  150,  119,  150,  150,  122,  123,  124,  125,   15,
+           16,   17,   18,   19,   20,   21,   22,   23,   24,   25,
+           26,   27,  102,  150,  150,  150,  154,  151,  151,  151,
+          151,  148,  149,  113,  114,  151,  151,  151,  151,  151,
+          151,  151,  122,  123,  124,  125,  151,  151,  151,  151,
+           56,   57,  151,  151,  151,  151,  151,  151,  151,  151,
+          151,   67,  151,  151,  151,  155,  152,  152,  152,  152,
+          152,  152,   -1,  152,  152,  152,  152,  152,  152,  152,
+          152,  152,  152,  152,  152,  152,  152,  152,  152,  152,
+          152,   -1,  153,   -1,  154,  154,  154,  154,  154,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,  128,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,  142,   -1,   -1,   -1,
+           -1,   -1,   -1,  149
+    );
+
+    protected $actionBase = array(
+            0,  252,  222,  397,  248,  339,  337,  241,  753,  754,
+          568,  581,  565,  728,  723,  722,  378,  577,  720,  719,
+          716,  469,  345,  636,  154,  154,  154,  154,   62,  498,
+          800,  492,  800,  563,   59,  638,  638,  638,  281,  355,
+          634,  634,  634,  485,  559,  708,  430,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,  763,  763,
+          763,  763,  763,  763,  763,  763,  763,  763,   88,  657,
+          182,  653,  802,  807,  804,  799,  798,  599,  803,  808,
+          696,  689,  385,  691,  692,  693,  695,  805,  820,  801,
+          806,  293,  293,  293,  293,  293,  293,  293,  293,  293,
+          293,  293,  293,  293,  293,  293,  293,   82,   14,  464,
+            8,    8,    8,    8,    8,    8,    8,    8,    8,    8,
+            8,    8,    8,    8,    8,    8,    8,    8,   87,   87,
+           87,  407,  682,  188,   11,  874,  159,   -2,   -2,   -2,
+           -2,   -2,  263,  263,  263,  263,   42,   42,  594,  594,
+          413,  341,  341,  341,  341,  341,  341,  341,  341,  341,
+          341,  711,  734,  733,  731,    2,    2,  -58,  458,  393,
+          393,  393,  393,   19,   -5,  240,   -5,  582,  790,  334,
+          226,  151,  151,  151,   83,  575,  155,  155,   31,   31,
+          384,  384,  423,  384,  499,  499,  499,   89,   89,   89,
+           89,  250,   89,   89,   89,  637,  560,  -65,  572,  730,
+          483,  566,  729,  -19,  356,  405,  557,  556,   48,  592,
+           48,  520,  474,  421,  589,   48,   48,   50,   88,  489,
+          395,  624,  440,  146,  640,  380,  348,  -14,  480,  439,
+          677,  639,  795,  451,  794,  346,  406,   38,  395,  395,
+          395,  353,  679,  686,  412,  361,  710,  687,  416,  709,
+          700,  144,   85,  755,  357,  656,  473,  473,  473,  473,
+          473,  473,  386,  473,  506,  768,  768,  562,  571,  386,
+          791,  386,  473,  768,  386,   99,  386,  580,  473,  578,
+          578,  506,  574,  570,  768,  768,  570,  562,  386,  620,
+          654,  587,  618,  396,  396,  587,  386,  396,  571,  396,
+           40,  789,  788,  332,  787,  792,  786,  759,  785,  518,
+          688,  552,  576,  777,  776,  784,  497,  564,  778,  793,
+          584,  579,  542,  369,  583,  403,  655,  592,  591,  368,
+          368,  368,  403,  757,  368,  368,  368,  368,  368,  368,
+          368,  368,  752,  602,  389,  607,  619,  625,  373,  626,
+          604,  586,  392,  617,  561,  584,  584,  819,  685,  422,
+          775,  810,  783,  622,  109,  363,  770,  608,  536,  555,
+          769,  662,  809,  547,  702,  584,  368,  758,  767,  824,
+          823,  756,  822,  815,  139,  603,  627,   64,  821,  665,
+          666,  621,  818,  814,  813,  593,   64,  630,  796,  764,
+          573,  765,  668,  331,  285,  704,  782,  632,  817,  816,
+          797,  633,  642,  669,  674,  302,  781,  398,  390,  643,
+          567,  569,  675,  766,  647,  649,  811,  585,  591,  590,
+          588,  595,  676,  812,  415,  651,  628,  629,  623,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  154,  154,
+          154,    0,    0,    0,    0,  154,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,  293,  293,  293,  293,  293,
+          293,  293,  293,  293,  293,  293,  293,  293,  293,  293,
+          293,  293,  293,  293,  293,  293,  293,  293,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,  293,  293,  293,  293,  293,  293,
+          293,  293,  293,  293,  293,  293,  293,  293,  293,  293,
+          293,  293,  293,  293,  293,  293,  293,  293,  293,  293,
+          293,  293,    0,  293,  293,  293,  293,  293,  293,  151,
+          151,  151,  151,  341,  341,  341,  341,  341,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,   97,   97,
+           97,   97,  151,  151,  341,   97,  473,  473,  473,  473,
+          341,  473,  155,  473,  155,  155,    0,    0,    0,    0,
+            0,  473,  155,    0,    0,   48,   48,    0,    0,    0,
+            0,  473,  473,  473,  473,  473,  473,  473,  473,  473,
+          473,  473,   48,  155,    0,  484,  484,   64,  484,  484,
+            0,    0,    0,  473,  473,    0,  574,    0,    0,    0,
+            0,  768,    0,    0,    0,    0,    0,  368,  109,  775,
+            0,   74,    0,    0,    0,    0,    0,  422,   74,  223,
+            0,  223,    0,    0,    0,  368,  368,  368,    0,  422,
+          422,    0,    0,  133,  422,    0,  133,   66,    0,    0,
+           66,    0,   64
+    );
+
+    protected $actionDefault = array(
+            3,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+          451,  451,  411,32767,32767,32767,32767,  277,  277,  277,
+        32767,  412,  412,  412,  412,  412,  412,  412,32767,32767,
+        32767,32767,32767,  356,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+          456,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,  339,  340,  342,  343,  276,  413,  229,  455,  275,
+          115,  238,  231,  186,  118,  274,  217,  304,  357,  306,
+          355,  359,  305,  281,  286,  287,  288,  289,  290,  291,
+          292,  293,  294,  295,  296,  297,  280,  358,  336,  335,
+          334,  302,  303,  307,  309,  279,  308,  325,  326,  323,
+          324,  327,  328,  329,  330,  331,32767,32767,  450,  450,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,  261,  261,  261,  261,  316,  317,32767,  262,  221,
+          221,  221,  221,32767,  221,32767,32767,32767,32767,  404,
+          333,  311,  312,  310,32767,  384,32767,  386,32767,32767,
+          299,  301,  379,  282,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,  381,  414,  414,32767,32767,32767,  373,
+        32767,  154,  205,  207,  389,32767,32767,32767,32767,32767,
+          321,32767,32767,32767,32767,32767,32767,  464,32767,32767,
+        32767,32767,32767,  414,32767,  414,32767,32767,  313,  314,
+          315,32767,32767,32767,  414,  414,32767,32767,  414,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+        32767,32767,  158,32767,32767,  387,  387,32767,32767,  158,
+          382,  158,32767,32767,  158,  410,  158,  171,32767,  169,
+          169,32767,32767,  173,32767,  428,  173,32767,  158,  191,
+          191,  365,  160,  223,  223,  365,  158,  223,32767,  223,
+        32767,32767,32767,   81,32767,32767,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+          375,32767,32767,32767,32767,  405,  426,  373,32767,  319,
+          320,  322,32767,  416,  344,  345,  346,  347,  348,  349,
+          350,  352,32767,  378,32767,32767,32767,32767,32767,32767,
+           83,  107,  237,32767,  463,   83,  376,  463,32767,32767,
+        32767,32767,32767,32767,  278,32767,32767,   83,32767,   83,
+        32767,32767,32767,32767,  414,  377,  318,  390,  432,32767,
+        32767,  415,32767,32767,  212,   83,32767,  172,32767,32767,
+        32767,32767,32767,32767,32767,32767,  174,32767,  414,32767,
+        32767,32767,32767,32767,  273,32767,32767,32767,32767,32767,
+          414,32767,32767,32767,32767,  216,32767,32767,32767,32767,
+        32767,32767,32767,32767,32767,32767,32767,   81,   59,32767,
+          255,32767,32767,32767,32767,32767,32767,32767,32767,  120,
+          120,    3,  240,    3,  151,  240,  120,  199,  120,  120,
+          240,  240,  191,  191,  120,  120,  247,  120,  120,  120,
+          120,  120,  120,  120,  120,  120,  120
+    );
+
+    protected $goto = array(
+          177,  177,  150,  150,  150,  160,  162,  193,  178,  175,
+          175,  175,  175,  176,  176,  176,  176,  176,  176,  176,
+          171,  172,  173,  174,  190,  188,  191,  419,  420,  310,
+          421,  424,  425,  426,  427,  428,  429,  430,  431,  842,
+          151,  152,  153,  154,  155,  156,  157,  158,  159,  161,
+          187,  189,  192,  208,  211,  212,  213,  214,  216,  217,
+          218,  219,  220,  221,  222,  223,  224,  225,  245,  246,
+          261,  262,  263,  328,  329,  330,  466,  194,  195,  196,
+          197,  198,  199,  200,  201,  202,  203,  204,  205,  206,
+          163,  207,  164,  179,  180,  181,  209,  182,  165,  166,
+          167,  183,  168,  210,  148,  184,  185,  169,  186,  170,
+          519,  440,  436,  439,  451,  469,  470,  472,    4,  289,
+            5,  445,  445,  445,  242,   23,  468,  673,  445,  243,
+          244,    9,  927,  768,  768, 1006, 1006,    6,  527,   10,
+           11,  433,  738,  459,  445,   12,   13,  433,    1,   14,
+           15,   16,   17,    2,   18,   19,    7,   20,  422,  422,
+          422,  422,  422,  422,  422,  422,  422,  422,  422,  422,
+          422,  422,  422,  335,  500,  325,  325,  273,  323,  323,
+          270,  271,  294,  464,  334,  295,  338,  488,  643,  643,
+          643,  928,  699,  445,  445,  457,  475,  445,  445,  462,
+          445,  785,  929,  982,  311,  641,  641,  641,  666,  446,
+          642,  642,  642,  481,  498,  465,  423,  423,  423,  423,
+          423,  423,  423,  423,  423,  423,  423,  423,  423,  423,
+          423,  754,  736,  734,  736,  537, 1010,  434,  763,  758,
+          666,  666,  312,  267,  258,  450,  499,  460,  467,  813,
+          528, 1016, 1016,  513,  811, 1017, 1017,  504, 1003,  382,
+          512, 1016,  309,  840,  324, 1017,  889,   26,  483,  484,
+          523,  486,  290,  345,  772,  296,  993,  781,  339,  321,
+         1019,  291,  292,  318,  662,  660,  789,  547,  343,  920,
+          659,  659,  667,  667,  667,  669,  925,  658,  376,  792,
+          670,  480,  387,  742,  829,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,  931,    0,  775,  775,  775,
+          775,  931,  775,    0,  775,  821,    0,    0,    0,    0,
+            0,    0,  990,    0,    0,    0,    0,    0,  990,    0,
+            0,    0,    0,    0,    0,  732,  732,  732,  732,    0,
+          727,  733,  511, 1001, 1001,    0,    0,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+          988,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+            0,    0,    0,  791,    0,  791,    0,    0,    0,    0,
+          994,  995
+    );
+
+    protected $gotoCheck = array(
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           49,    7,   36,   36,   36,   36,   36,   36,    2,   61,
+            2,    7,    7,    7,   57,   87,   75,   24,    7,   57,
+           57,   25,   71,   66,   66,   66,   66,   25,    4,   25,
+           25,  104,   27,    7,    7,   25,   25,  104,   25,   25,
+           25,   25,   25,   25,   25,   25,   25,   25,  108,  108,
+          108,  108,  108,  108,  108,  108,  108,  108,  108,  108,
+          108,  108,  108,   43,   43,   43,   43,   43,   43,   43,
+           43,   43,   43,   43,   43,   43,   43,   43,   11,   11,
+           11,   71,   42,    7,    7,    7,    7,    7,    7,  110,
+            7,   73,   71,   71,   39,    9,    9,    9,   18,    7,
+           10,   10,   10,   33,   33,    7,  109,  109,  109,  109,
+          109,  109,  109,  109,  109,  109,  109,  109,  109,  109,
+          109,    9,    9,    9,    9,    9,  118,    9,    9,    9,
+           18,   18,   50,  107,  107,   50,   41,   50,    6,    6,
+            6,  119,  119,    6,    6,  120,  120,   53,  116,   50,
+           53,  119,   53,   90,   40,  120,   91,   50,   52,   52,
+           52,   59,   59,   59,   68,   13,  113,   70,   17,   12,
+          119,   61,   61,    8,   20,   19,   74,   63,   16,   97,
+           18,   18,   18,   18,   18,   18,   99,   18,   55,   76,
+           21,   56,   95,   60,   89,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   49,   -1,   49,   49,   49,
+           49,   49,   49,   -1,   49,   87,   -1,   -1,   -1,   -1,
+           -1,   -1,   75,   -1,   -1,   -1,   -1,   -1,   75,   -1,
+           -1,   -1,   -1,   -1,   -1,   49,   49,   49,   49,   -1,
+           49,   49,   49,   75,   75,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           75,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+           -1,   -1,   -1,   75,   -1,   75,   -1,   -1,   -1,   -1,
+           75,   75
+    );
+
+    protected $gotoBase = array(
+            0,    0, -413,    0,  110,    0,  227, -153,    2,  202,
+          207,  185,  -17,   11,    0,    0,  -59,    3,  -56,  -12,
+            8,  -71,  -38,    0,  102, -399,    0,  108,    0,    0,
+            0,    0,    0,  165,    0,    0,   60,    0,    0,  154,
+           32,   19,  160,  -53,    0,    0,    0,    0,    0,   88,
+         -119,    0,   18, -127,    0,  -72,  -75, -411,    0,   -7,
+          -74, -261,    0,  -21,    0,    0,  -93,    0,   20,    0,
+           21, -172,    0,  162,  -14,  105,  -73,    0,    0,    0,
+            0,    0,    0,    0,    0,    0,    0,   97,    0,  -84,
+           33,   13,    0,    0,    0,  -83,    0,  -67,    0,  -63,
+            0,    0,    0,    0, -106,    0,    0,  -22,  -68,  -10,
+          158,    0,    0,    7,    0,    0,   29,    0,  194,   -6,
+           -2,    0
+    );
+
+    protected $gotoDefault = array(
+        -32768,  390,    3,  559,  629,  637,  493,  410,  441,  678,
+          679,  680,  314,  351,  411,  313,  340,  336,  668,  661,
+          663,  671,  149,  341,  674,    8,  676,  706,  302,  683,
+          303,  508,  685,  453,  687,  688,  435,  315,  316,  454,
+          322,  482,  698,  215,  319,  700,  301,  701,  709,  304,
+          510,  490,  473,  503,  412,  373,  479,  241,  461,  477,
+          741,  288,  749,  552,  757,  760,  413,  474,  771,  378,
+          779,  945,  331,  784,  790,  977,  793,  796,  358,  342,
+          485,  800,  801,   22,  805,  520,  521,  820,  248,  828,
+          841,  356,  908,  910,  448,  384,  921,  370,  344,  924,
+          981,  363,  414,  374,  937,  272,  293,  257,  415,  259,
+          432, 1009,  416,  375,  984,  326, 1004,  365, 1011, 1018,
+          286,  478
+    );
+
+    protected $ruleToNonTerminal = array(
+            0,    1,    2,    2,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+            4,    4,    5,    5,    5,    5,    5,    5,    5,    6,
+            6,    7,    7,    8,    3,    3,    3,    3,    3,    3,
+            3,    3,    3,    3,    3,   13,   13,   14,   14,   14,
+           14,   16,   16,   12,   12,   17,   17,   18,   18,   19,
+           19,   20,   20,   15,   15,   21,   23,   23,   24,   25,
+           25,   26,   26,   26,   26,   27,   27,   27,   27,   27,
+           27,   27,   27,   27,   27,   27,   27,   27,   27,   27,
+           27,   27,   27,   27,   27,   27,   27,   27,   27,    9,
+            9,   46,   46,   48,   47,   47,   40,   40,   50,   50,
+           51,   51,   10,   11,   11,   11,   54,   54,   54,   55,
+           55,   58,   58,   56,   56,   59,   59,   34,   34,   42,
+           42,   45,   45,   45,   44,   44,   60,   35,   35,   35,
+           35,   61,   61,   62,   62,   63,   63,   32,   32,   28,
+           28,   64,   30,   30,   65,   29,   29,   31,   31,   41,
+           41,   41,   52,   52,   67,   67,   68,   68,   70,   70,
+           70,   69,   69,   53,   53,   71,   71,   72,   72,   73,
+           73,   73,   37,   37,   74,   38,   38,   76,   76,   57,
+           57,   77,   77,   77,   77,   82,   82,   83,   83,   84,
+           84,   84,   84,   84,   85,   86,   86,   81,   81,   78,
+           78,   80,   80,   88,   88,   87,   87,   87,   87,   87,
+           87,   79,   79,   89,   89,   39,   39,   33,   33,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   22,   22,   22,   22,   22,   22,   22,   22,
+           22,   22,   96,   90,   90,   95,   95,   98,   98,   99,
+          100,  100,  100,  104,  104,   49,   49,   49,   91,   91,
+          102,  102,   92,   92,   94,   94,   94,   97,   97,  108,
+          108,  109,  109,  109,   93,   93,   93,   93,   93,   93,
+           93,   93,   93,   93,   93,   93,   93,   93,   93,   93,
+          111,  111,   36,   36,  106,  106,  106,  101,  101,  101,
+          112,  112,  112,  112,  112,  112,   43,   43,   43,   75,
+           75,   75,  114,  105,  105,  105,  105,  105,  105,  103,
+          103,  103,  113,  113,  113,   66,  115,  115,  116,  116,
+          116,  110,  110,  117,  117,  118,  118,  118,  118,  107,
+          107,  107,  107,  120,  119,  119,  119,  119,  119,  119,
+          119,  121,  121,  121
+    );
+
+    protected $ruleToLength = array(
+            1,    1,    2,    0,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    3,    1,    1,    1,    1,    1,    3,    5,
+            4,    3,    4,    2,    3,    1,    1,    7,    8,    6,
+            7,    3,    1,    3,    1,    3,    1,    1,    3,    1,
+            2,    1,    2,    3,    1,    3,    3,    1,    3,    2,
+            0,    1,    1,    1,    1,    3,    7,   10,    5,    7,
+            9,    5,    3,    3,    3,    3,    3,    3,    1,    2,
+            5,    7,    9,    5,    6,    3,    3,    2,    1,    1,
+            1,    0,    2,    8,    0,    4,    1,    3,    0,    1,
+            0,    1,   10,    7,    6,    5,    1,    2,    2,    0,
+            2,    0,    2,    0,    2,    1,    3,    1,    4,    1,
+            4,    1,    1,    4,    1,    3,    3,    3,    4,    4,
+            5,    0,    2,    4,    3,    1,    1,    1,    4,    0,
+            2,    5,    0,    2,    6,    0,    2,    0,    3,    1,
+            2,    1,    1,    0,    1,    3,    4,    6,    1,    1,
+            1,    0,    1,    0,    2,    2,    3,    1,    3,    1,
+            2,    2,    3,    1,    1,    3,    1,    1,    3,    2,
+            0,    3,    3,    9,    3,    1,    3,    0,    2,    4,
+            5,    4,    4,    4,    3,    1,    1,    1,    3,    1,
+            1,    0,    1,    1,    2,    1,    1,    1,    1,    1,
+            1,    1,    3,    1,    3,    3,    1,    0,    1,    1,
+            3,    3,    4,    4,    1,    2,    3,    3,    3,    3,
+            3,    3,    3,    3,    3,    3,    3,    3,    2,    2,
+            2,    2,    3,    3,    3,    3,    3,    3,    3,    3,
+            3,    3,    3,    3,    3,    3,    3,    3,    3,    2,
+            2,    2,    2,    3,    3,    3,    3,    3,    3,    3,
+            3,    3,    3,    3,    5,    4,    3,    4,    4,    2,
+            2,    4,    2,    2,    2,    2,    2,    2,    2,    2,
+            2,    2,    2,    1,    3,    2,    1,    2,    4,    2,
+           10,   11,    7,    3,    2,    0,    4,    1,    3,    2,
+            2,    2,    4,    1,    1,    1,    2,    3,    1,    1,
+            1,    1,    0,    3,    0,    1,    1,    0,    1,    1,
+            3,    4,    3,    1,    1,    1,    1,    1,    1,    1,
+            1,    1,    1,    1,    1,    1,    3,    2,    3,    3,
+            0,    1,    0,    1,    1,    3,    1,    1,    3,    1,
+            1,    4,    4,    4,    1,    4,    1,    1,    3,    1,
+            4,    2,    3,    1,    4,    4,    3,    3,    3,    1,
+            3,    1,    1,    3,    1,    4,    3,    1,    1,    1,
+            0,    0,    2,    3,    1,    3,    1,    4,    2,    2,
+            2,    1,    2,    1,    1,    4,    3,    3,    3,    6,
+            3,    1,    1,    1
+    );
+
+    protected function reduceRule0() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule1() {
+         $this->semValue = $this->handleNamespaces($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule2() {
+         if (is_array($this->semStack[$this->stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]); } else { $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)]; };
+    }
+
+    protected function reduceRule3() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule4() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule5() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule6() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule7() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule8() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule9() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule10() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule11() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule12() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule13() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule14() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule15() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule16() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule17() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule18() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule19() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule20() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule21() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule22() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule23() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule24() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule25() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule26() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule27() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule28() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule29() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule30() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule31() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule32() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule33() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule34() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule35() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule36() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule37() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule38() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule39() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule40() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule41() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule42() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule43() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule44() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule45() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule46() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule47() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule48() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule49() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule50() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule51() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule52() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule53() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule54() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule55() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule56() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule57() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule58() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule59() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule60() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule61() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule62() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule63() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule64() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule65() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule66() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule67() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule68() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule69() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule70() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule71() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule72() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule73() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule74() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule75() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule76() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule77() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule78() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule79() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule80() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule81() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule82() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule83() {
+         $this->semValue = new Name($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule84() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule85() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule86() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule87() {
+         $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule88() {
+         $this->semValue = new Stmt\Namespace_($this->semStack[$this->stackPos-(3-2)], null, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule89() {
+         $this->semValue = new Stmt\Namespace_($this->semStack[$this->stackPos-(5-2)], $this->semStack[$this->stackPos-(5-4)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule90() {
+         $this->semValue = new Stmt\Namespace_(null, $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule91() {
+         $this->semValue = new Stmt\Use_($this->semStack[$this->stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule92() {
+         $this->semValue = new Stmt\Use_($this->semStack[$this->stackPos-(4-3)], $this->semStack[$this->stackPos-(4-2)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule93() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule94() {
+         $this->semValue = new Stmt\Const_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule95() {
+         $this->semValue = Stmt\Use_::TYPE_FUNCTION;
+    }
+
+    protected function reduceRule96() {
+         $this->semValue = Stmt\Use_::TYPE_CONSTANT;
+    }
+
+    protected function reduceRule97() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], $this->semStack[$this->stackPos-(7-2)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule98() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(8-4)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(8-7)], $this->semStack[$this->stackPos-(8-2)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule99() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule100() {
+         $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule101() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule102() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule103() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule104() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule105() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule106() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule107() {
+         $this->semValue = new Stmt\UseUse($this->semStack[$this->stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule108() {
+         $this->semValue = new Stmt\UseUse($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule109() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule110() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule111() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)]; $this->semValue->type = Stmt\Use_::TYPE_NORMAL;
+    }
+
+    protected function reduceRule112() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)]; $this->semValue->type = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule113() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule114() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule115() {
+         $this->semValue = new Node\Const_($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule116() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule117() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule118() {
+         $this->semValue = new Node\Const_($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule119() {
+         if (is_array($this->semStack[$this->stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]); } else { $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)]; };
+    }
+
+    protected function reduceRule120() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule121() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule122() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule123() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule124() {
+         throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule125() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule126() {
+         $this->semValue = new Stmt\If_($this->semStack[$this->stackPos-(7-3)], ['stmts' => is_array($this->semStack[$this->stackPos-(7-5)]) ? $this->semStack[$this->stackPos-(7-5)] : array($this->semStack[$this->stackPos-(7-5)]), 'elseifs' => $this->semStack[$this->stackPos-(7-6)], 'else' => $this->semStack[$this->stackPos-(7-7)]], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule127() {
+         $this->semValue = new Stmt\If_($this->semStack[$this->stackPos-(10-3)], ['stmts' => $this->semStack[$this->stackPos-(10-6)], 'elseifs' => $this->semStack[$this->stackPos-(10-7)], 'else' => $this->semStack[$this->stackPos-(10-8)]], $this->startAttributeStack[$this->stackPos-(10-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule128() {
+         $this->semValue = new Stmt\While_($this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-5)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule129() {
+         $this->semValue = new Stmt\Do_($this->semStack[$this->stackPos-(7-5)], is_array($this->semStack[$this->stackPos-(7-2)]) ? $this->semStack[$this->stackPos-(7-2)] : array($this->semStack[$this->stackPos-(7-2)]), $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule130() {
+         $this->semValue = new Stmt\For_(['init' => $this->semStack[$this->stackPos-(9-3)], 'cond' => $this->semStack[$this->stackPos-(9-5)], 'loop' => $this->semStack[$this->stackPos-(9-7)], 'stmts' => $this->semStack[$this->stackPos-(9-9)]], $this->startAttributeStack[$this->stackPos-(9-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule131() {
+         $this->semValue = new Stmt\Switch_($this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-5)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule132() {
+         $this->semValue = new Stmt\Break_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule133() {
+         $this->semValue = new Stmt\Continue_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule134() {
+         $this->semValue = new Stmt\Return_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule135() {
+         $this->semValue = new Stmt\Global_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule136() {
+         $this->semValue = new Stmt\Static_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule137() {
+         $this->semValue = new Stmt\Echo_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule138() {
+         $this->semValue = new Stmt\InlineHTML($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule139() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule140() {
+         $this->semValue = new Stmt\Unset_($this->semStack[$this->stackPos-(5-3)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule141() {
+         $this->semValue = new Stmt\Foreach_($this->semStack[$this->stackPos-(7-3)], $this->semStack[$this->stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$this->stackPos-(7-5)][1], 'stmts' => $this->semStack[$this->stackPos-(7-7)]], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule142() {
+         $this->semValue = new Stmt\Foreach_($this->semStack[$this->stackPos-(9-3)], $this->semStack[$this->stackPos-(9-7)][0], ['keyVar' => $this->semStack[$this->stackPos-(9-5)], 'byRef' => $this->semStack[$this->stackPos-(9-7)][1], 'stmts' => $this->semStack[$this->stackPos-(9-9)]], $this->startAttributeStack[$this->stackPos-(9-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule143() {
+         $this->semValue = new Stmt\Declare_($this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-5)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule144() {
+         $this->semValue = new Stmt\TryCatch($this->semStack[$this->stackPos-(6-3)], $this->semStack[$this->stackPos-(6-5)], $this->semStack[$this->stackPos-(6-6)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule145() {
+         $this->semValue = new Stmt\Throw_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule146() {
+         $this->semValue = new Stmt\Goto_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule147() {
+         $this->semValue = new Stmt\Label($this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule148() {
+         $this->semValue = array(); /* means: no statement */
+    }
+
+    protected function reduceRule149() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule150() {
+         $this->semValue = array(); /* means: no statement */
+    }
+
+    protected function reduceRule151() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule152() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule153() {
+         $this->semValue = new Stmt\Catch_($this->semStack[$this->stackPos-(8-3)], substr($this->semStack[$this->stackPos-(8-4)], 1), $this->semStack[$this->stackPos-(8-7)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule154() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule155() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-3)];
+    }
+
+    protected function reduceRule156() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule157() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule158() {
+         $this->semValue = false;
+    }
+
+    protected function reduceRule159() {
+         $this->semValue = true;
+    }
+
+    protected function reduceRule160() {
+         $this->semValue = false;
+    }
+
+    protected function reduceRule161() {
+         $this->semValue = true;
+    }
+
+    protected function reduceRule162() {
+         $this->semValue = new Stmt\Function_($this->semStack[$this->stackPos-(10-3)], ['byRef' => $this->semStack[$this->stackPos-(10-2)], 'params' => $this->semStack[$this->stackPos-(10-5)], 'returnType' => $this->semStack[$this->stackPos-(10-7)], 'stmts' => $this->semStack[$this->stackPos-(10-9)]], $this->startAttributeStack[$this->stackPos-(10-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule163() {
+         $this->semValue = new Stmt\Class_($this->semStack[$this->stackPos-(7-2)], ['type' => $this->semStack[$this->stackPos-(7-1)], 'extends' => $this->semStack[$this->stackPos-(7-3)], 'implements' => $this->semStack[$this->stackPos-(7-4)], 'stmts' => $this->semStack[$this->stackPos-(7-6)]], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule164() {
+         $this->semValue = new Stmt\Interface_($this->semStack[$this->stackPos-(6-2)], ['extends' => $this->semStack[$this->stackPos-(6-3)], 'stmts' => $this->semStack[$this->stackPos-(6-5)]], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule165() {
+         $this->semValue = new Stmt\Trait_($this->semStack[$this->stackPos-(5-2)], $this->semStack[$this->stackPos-(5-4)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule166() {
+         $this->semValue = 0;
+    }
+
+    protected function reduceRule167() {
+         $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT;
+    }
+
+    protected function reduceRule168() {
+         $this->semValue = Stmt\Class_::MODIFIER_FINAL;
+    }
+
+    protected function reduceRule169() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule170() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule171() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule172() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule173() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule174() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule175() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule176() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule177() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule178() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule179() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule180() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule181() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule182() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule183() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule184() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule185() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule186() {
+         $this->semValue = new Stmt\DeclareDeclare($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule187() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule188() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-3)];
+    }
+
+    protected function reduceRule189() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule190() {
+         $this->semValue = $this->semStack[$this->stackPos-(5-3)];
+    }
+
+    protected function reduceRule191() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule192() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule193() {
+         $this->semValue = new Stmt\Case_($this->semStack[$this->stackPos-(4-2)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule194() {
+         $this->semValue = new Stmt\Case_(null, $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule195() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule196() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule197() {
+         $this->semValue = is_array($this->semStack[$this->stackPos-(1-1)]) ? $this->semStack[$this->stackPos-(1-1)] : array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule198() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-2)];
+    }
+
+    protected function reduceRule199() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule200() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule201() {
+         $this->semValue = new Stmt\ElseIf_($this->semStack[$this->stackPos-(5-3)], is_array($this->semStack[$this->stackPos-(5-5)]) ? $this->semStack[$this->stackPos-(5-5)] : array($this->semStack[$this->stackPos-(5-5)]), $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule202() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule203() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule204() {
+         $this->semValue = new Stmt\ElseIf_($this->semStack[$this->stackPos-(6-3)], $this->semStack[$this->stackPos-(6-6)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule205() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule206() {
+         $this->semValue = new Stmt\Else_(is_array($this->semStack[$this->stackPos-(2-2)]) ? $this->semStack[$this->stackPos-(2-2)] : array($this->semStack[$this->stackPos-(2-2)]), $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule207() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule208() {
+         $this->semValue = new Stmt\Else_($this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule209() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)], false);
+    }
+
+    protected function reduceRule210() {
+         $this->semValue = array($this->semStack[$this->stackPos-(2-2)], true);
+    }
+
+    protected function reduceRule211() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)], false);
+    }
+
+    protected function reduceRule212() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule213() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule214() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule215() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule216() {
+         $this->semValue = new Node\Param(substr($this->semStack[$this->stackPos-(4-4)], 1), null, $this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-2)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule217() {
+         $this->semValue = new Node\Param(substr($this->semStack[$this->stackPos-(6-4)], 1), $this->semStack[$this->stackPos-(6-6)], $this->semStack[$this->stackPos-(6-1)], $this->semStack[$this->stackPos-(6-2)], $this->semStack[$this->stackPos-(6-3)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule218() {
+         $this->semValue = $this->handleScalarTypes($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule219() {
+         $this->semValue = 'array';
+    }
+
+    protected function reduceRule220() {
+         $this->semValue = 'callable';
+    }
+
+    protected function reduceRule221() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule222() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule223() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule224() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule225() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule226() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule227() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule228() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule229() {
+         $this->semValue = new Node\Arg($this->semStack[$this->stackPos-(1-1)], false, false, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule230() {
+         $this->semValue = new Node\Arg($this->semStack[$this->stackPos-(2-2)], true, false, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule231() {
+         $this->semValue = new Node\Arg($this->semStack[$this->stackPos-(2-2)], false, true, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule232() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule233() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule234() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule235() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule236() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule237() {
+         $this->semValue = new Stmt\StaticVar(substr($this->semStack[$this->stackPos-(1-1)], 1), null, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule238() {
+         $this->semValue = new Stmt\StaticVar(substr($this->semStack[$this->stackPos-(3-1)], 1), $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule239() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule240() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule241() {
+         $this->semValue = new Stmt\Property($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule242() {
+         $this->semValue = new Stmt\ClassConst($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule243() {
+         $this->semValue = new Stmt\ClassMethod($this->semStack[$this->stackPos-(9-4)], ['type' => $this->semStack[$this->stackPos-(9-1)], 'byRef' => $this->semStack[$this->stackPos-(9-3)], 'params' => $this->semStack[$this->stackPos-(9-6)], 'returnType' => $this->semStack[$this->stackPos-(9-8)], 'stmts' => $this->semStack[$this->stackPos-(9-9)]], $this->startAttributeStack[$this->stackPos-(9-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule244() {
+         $this->semValue = new Stmt\TraitUse($this->semStack[$this->stackPos-(3-2)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule245() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule246() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule247() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule248() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule249() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule250() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(5-1)][0], $this->semStack[$this->stackPos-(5-1)][1], $this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-4)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule251() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], $this->semStack[$this->stackPos-(4-3)], null, $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule252() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], null, $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule253() {
+         $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$this->stackPos-(4-1)][0], $this->semStack[$this->stackPos-(4-1)][1], null, $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule254() {
+         $this->semValue = array($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)]);
+    }
+
+    protected function reduceRule255() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule256() {
+         $this->semValue = array(null, $this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule257() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule258() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule259() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule260() {
+         $this->semValue = 0;
+    }
+
+    protected function reduceRule261() {
+         $this->semValue = 0;
+    }
+
+    protected function reduceRule262() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule263() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule264() {
+         Stmt\Class_::verifyModifier($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]); $this->semValue = $this->semStack[$this->stackPos-(2-1)] | $this->semStack[$this->stackPos-(2-2)];
+    }
+
+    protected function reduceRule265() {
+         $this->semValue = Stmt\Class_::MODIFIER_PUBLIC;
+    }
+
+    protected function reduceRule266() {
+         $this->semValue = Stmt\Class_::MODIFIER_PROTECTED;
+    }
+
+    protected function reduceRule267() {
+         $this->semValue = Stmt\Class_::MODIFIER_PRIVATE;
+    }
+
+    protected function reduceRule268() {
+         $this->semValue = Stmt\Class_::MODIFIER_STATIC;
+    }
+
+    protected function reduceRule269() {
+         $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT;
+    }
+
+    protected function reduceRule270() {
+         $this->semValue = Stmt\Class_::MODIFIER_FINAL;
+    }
+
+    protected function reduceRule271() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule272() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule273() {
+         $this->semValue = new Stmt\PropertyProperty(substr($this->semStack[$this->stackPos-(1-1)], 1), null, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule274() {
+         $this->semValue = new Stmt\PropertyProperty(substr($this->semStack[$this->stackPos-(3-1)], 1), $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule275() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule276() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule277() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule278() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule279() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule280() {
+         $this->semValue = new Expr\Assign($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule281() {
+         $this->semValue = new Expr\Assign($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule282() {
+         $this->semValue = new Expr\AssignRef($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule283() {
+         $this->semValue = new Expr\AssignRef($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule284() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule285() {
+         $this->semValue = new Expr\Clone_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule286() {
+         $this->semValue = new Expr\AssignOp\Plus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule287() {
+         $this->semValue = new Expr\AssignOp\Minus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule288() {
+         $this->semValue = new Expr\AssignOp\Mul($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule289() {
+         $this->semValue = new Expr\AssignOp\Div($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule290() {
+         $this->semValue = new Expr\AssignOp\Concat($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule291() {
+         $this->semValue = new Expr\AssignOp\Mod($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule292() {
+         $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule293() {
+         $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule294() {
+         $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule295() {
+         $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule296() {
+         $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule297() {
+         $this->semValue = new Expr\AssignOp\Pow($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule298() {
+         $this->semValue = new Expr\PostInc($this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule299() {
+         $this->semValue = new Expr\PreInc($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule300() {
+         $this->semValue = new Expr\PostDec($this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule301() {
+         $this->semValue = new Expr\PreDec($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule302() {
+         $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule303() {
+         $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule304() {
+         $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule305() {
+         $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule306() {
+         $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule307() {
+         $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule308() {
+         $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule309() {
+         $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule310() {
+         $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule311() {
+         $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule312() {
+         $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule313() {
+         $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule314() {
+         $this->semValue = new Expr\BinaryOp\Div($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule315() {
+         $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule316() {
+         $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule317() {
+         $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule318() {
+         $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule319() {
+         $this->semValue = new Expr\UnaryPlus($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule320() {
+         $this->semValue = new Expr\UnaryMinus($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule321() {
+         $this->semValue = new Expr\BooleanNot($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule322() {
+         $this->semValue = new Expr\BitwiseNot($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule323() {
+         $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule324() {
+         $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule325() {
+         $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule326() {
+         $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule327() {
+         $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule328() {
+         $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule329() {
+         $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule330() {
+         $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule331() {
+         $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule332() {
+         $this->semValue = new Expr\Instanceof_($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule333() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule334() {
+         $this->semValue = new Expr\Ternary($this->semStack[$this->stackPos-(5-1)], $this->semStack[$this->stackPos-(5-3)], $this->semStack[$this->stackPos-(5-5)], $this->startAttributeStack[$this->stackPos-(5-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule335() {
+         $this->semValue = new Expr\Ternary($this->semStack[$this->stackPos-(4-1)], null, $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule336() {
+         $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule337() {
+         $this->semValue = new Expr\Isset_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule338() {
+         $this->semValue = new Expr\Empty_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule339() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule340() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule341() {
+         $this->semValue = new Expr\Eval_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule342() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule343() {
+         $this->semValue = new Expr\Include_($this->semStack[$this->stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule344() {
+         $this->semValue = new Expr\Cast\Int_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule345() {
+         $this->semValue = new Expr\Cast\Double($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule346() {
+         $this->semValue = new Expr\Cast\String_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule347() {
+         $this->semValue = new Expr\Cast\Array_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule348() {
+         $this->semValue = new Expr\Cast\Object_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule349() {
+         $this->semValue = new Expr\Cast\Bool_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule350() {
+         $this->semValue = new Expr\Cast\Unset_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule351() {
+         $this->semValue = new Expr\Exit_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule352() {
+         $this->semValue = new Expr\ErrorSuppress($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule353() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule354() {
+         $this->semValue = new Expr\ShellExec($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule355() {
+         $this->semValue = new Expr\Print_($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule356() {
+         $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule357() {
+         $this->semValue = new Expr\Yield_($this->semStack[$this->stackPos-(2-2)], null, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule358() {
+         $this->semValue = new Expr\Yield_($this->semStack[$this->stackPos-(4-4)], $this->semStack[$this->stackPos-(4-2)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule359() {
+         $this->semValue = new Expr\YieldFrom($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule360() {
+         $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$this->stackPos-(10-2)], 'params' => $this->semStack[$this->stackPos-(10-4)], 'uses' => $this->semStack[$this->stackPos-(10-6)], 'returnType' => $this->semStack[$this->stackPos-(10-7)], 'stmts' => $this->semStack[$this->stackPos-(10-9)]], $this->startAttributeStack[$this->stackPos-(10-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule361() {
+         $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$this->stackPos-(11-3)], 'params' => $this->semStack[$this->stackPos-(11-5)], 'uses' => $this->semStack[$this->stackPos-(11-7)], 'returnType' => $this->semStack[$this->stackPos-(11-8)], 'stmts' => $this->semStack[$this->stackPos-(11-10)]], $this->startAttributeStack[$this->stackPos-(11-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule362() {
+         $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$this->stackPos-(7-3)], 'implements' => $this->semStack[$this->stackPos-(7-4)], 'stmts' => $this->semStack[$this->stackPos-(7-6)]], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-2)]);
+    }
+
+    protected function reduceRule363() {
+         $this->semValue = new Expr\New_($this->semStack[$this->stackPos-(3-2)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule364() {
+         list($class, $ctorArgs) = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule365() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule366() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-3)];
+    }
+
+    protected function reduceRule367() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule368() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule369() {
+         $this->semValue = new Expr\ClosureUse(substr($this->semStack[$this->stackPos-(2-2)], 1), $this->semStack[$this->stackPos-(2-1)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule370() {
+         $this->semValue = new Expr\FuncCall($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule371() {
+         $this->semValue = new Expr\FuncCall($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule372() {
+         $this->semValue = new Expr\StaticCall($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule373() {
+         $this->semValue = new Name($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule374() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule375() {
+         $this->semValue = new Name($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule376() {
+         $this->semValue = new Name\FullyQualified($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule377() {
+         $this->semValue = new Name\Relative($this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule378() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule379() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule380() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule381() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule382() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule383() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule384() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule385() {
+         $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$this->stackPos-(1-1)], '`'), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes));
+    }
+
+    protected function reduceRule386() {
+         foreach ($this->semStack[$this->stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', true); } }; $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule387() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule388() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule389() {
+         $this->semValue = new Expr\ConstFetch($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule390() {
+         $this->semValue = new Expr\ClassConstFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule391() {
+         $this->semValue = new Expr\Array_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule392() {
+         $this->semValue = new Expr\Array_($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule393() {
+         $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(1-1)]), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule394() {
+         $this->semValue = new Scalar\LNumber(Scalar\LNumber::parse($this->semStack[$this->stackPos-(1-1)]), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule395() {
+         $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$this->stackPos-(1-1)]), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule396() {
+         $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule397() {
+         $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule398() {
+         $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule399() {
+         $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule400() {
+         $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule401() {
+         $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule402() {
+         $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule403() {
+         $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule404() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule405() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule406() {
+         $this->semValue = new Scalar\String_(Scalar\String_::parseDocString($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)]), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule407() {
+         $this->semValue = new Scalar\String_('', $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule408() {
+         foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule409() {
+         foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, true); } } $s->value = preg_replace('~(\r\n|\n|\r)\z~', '', $s->value); if ('' === $s->value) array_pop($this->semStack[$this->stackPos-(3-2)]);; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule410() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule411() {
+        $this->semValue = $this->semStack[$this->stackPos];
+    }
+
+    protected function reduceRule412() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule413() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule414() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule415() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule416() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule417() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule418() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule419() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule420() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule421() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule422() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule423() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule424() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule425() {
+         $this->semValue = new Expr\MethodCall($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->semStack[$this->stackPos-(4-4)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule426() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule427() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule428() {
+         $this->semValue = new Expr\PropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule429() {
+         $this->semValue = substr($this->semStack[$this->stackPos-(1-1)], 1);
+    }
+
+    protected function reduceRule430() {
+         $this->semValue = $this->semStack[$this->stackPos-(4-3)];
+    }
+
+    protected function reduceRule431() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(2-2)], $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule432() {
+         $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule433() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule434() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule435() {
+         $this->semValue = new Expr\ArrayDimFetch($this->semStack[$this->stackPos-(4-1)], $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule436() {
+         $this->semValue = new Expr\PropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule437() {
+         $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule438() {
+         $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule439() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule440() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule441() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule442() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule443() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule444() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule445() {
+         $this->semValue = new Expr\List_($this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule446() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule447() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule448() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule449() {
+         $this->semValue = $this->semStack[$this->stackPos-(1-1)];
+    }
+
+    protected function reduceRule450() {
+         $this->semValue = null;
+    }
+
+    protected function reduceRule451() {
+         $this->semValue = array();
+    }
+
+    protected function reduceRule452() {
+         $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule453() {
+         $this->semStack[$this->stackPos-(3-1)][] = $this->semStack[$this->stackPos-(3-3)]; $this->semValue = $this->semStack[$this->stackPos-(3-1)];
+    }
+
+    protected function reduceRule454() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule455() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(3-3)], $this->semStack[$this->stackPos-(3-1)], false, $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule456() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(1-1)], null, false, $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule457() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(4-4)], $this->semStack[$this->stackPos-(4-1)], true, $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule458() {
+         $this->semValue = new Expr\ArrayItem($this->semStack[$this->stackPos-(2-2)], null, true, $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule459() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule460() {
+         $this->semStack[$this->stackPos-(2-1)][] = $this->semStack[$this->stackPos-(2-2)]; $this->semValue = $this->semStack[$this->stackPos-(2-1)];
+    }
+
+    protected function reduceRule461() {
+         $this->semValue = array($this->semStack[$this->stackPos-(1-1)]);
+    }
+
+    protected function reduceRule462() {
+         $this->semValue = array($this->semStack[$this->stackPos-(2-1)], $this->semStack[$this->stackPos-(2-2)]);
+    }
+
+    protected function reduceRule463() {
+         $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule464() {
+         $this->semValue = new Expr\Variable(substr($this->semStack[$this->stackPos-(1-1)], 1), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule465() {
+         $this->semValue = new Expr\ArrayDimFetch(new Expr\Variable(substr($this->semStack[$this->stackPos-(4-1)], 1), $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule466() {
+         $this->semValue = new Expr\PropertyFetch(new Expr\Variable(substr($this->semStack[$this->stackPos-(3-1)], 1), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(3-3)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule467() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule468() {
+         $this->semValue = new Expr\Variable($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule469() {
+         $this->semValue = new Expr\ArrayDimFetch(new Expr\Variable($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(6-4)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule470() {
+         $this->semValue = $this->semStack[$this->stackPos-(3-2)];
+    }
+
+    protected function reduceRule471() {
+         $this->semValue = new Scalar\String_($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule472() {
+         $this->semValue = new Scalar\String_($this->semStack[$this->stackPos-(1-1)], $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+
+    protected function reduceRule473() {
+         $this->semValue = new Expr\Variable(substr($this->semStack[$this->stackPos-(1-1)], 1), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Tokens.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Tokens.php
new file mode 100644
index 00000000000..861663fae02
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Parser/Tokens.php
@@ -0,0 +1,144 @@
+lexer = $lexer;
+        $this->errors = array();
+        $this->throwOnError = isset($options['throwOnError']) ? $options['throwOnError'] : true;
+    }
+
+    /**
+     * Get array of errors that occurred during the last parse.
+     *
+     * This method may only return multiple errors if the 'throwOnError' option is disabled.
+     *
+     * @return Error[]
+     */
+    public function getErrors() {
+        return $this->errors;
+    }
+
+    /**
+     * Parses PHP code into a node tree.
+     *
+     * @param string $code The source code to parse
+     *
+     * @return Node[]|null Array of statements (or null if the 'throwOnError' option is disabled and the parser was
+     *                     unable to recover from an error).
+     */
+    public function parse($code) {
+        $this->lexer->startLexing($code);
+        $this->errors = array();
+
+        // We start off with no lookahead-token
+        $symbol = self::SYMBOL_NONE;
+
+        // The attributes for a node are taken from the first and last token of the node.
+        // From the first token only the startAttributes are taken and from the last only
+        // the endAttributes. Both are merged using the array union operator (+).
+        $startAttributes = '*POISON';
+        $endAttributes = '*POISON';
+        $this->endAttributes = $endAttributes;
+
+        // In order to figure out the attributes for the starting token, we have to keep
+        // them in a stack
+        $this->startAttributeStack = array();
+
+        // Start off in the initial state and keep a stack of previous states
+        $state = 0;
+        $stateStack = array($state);
+
+        // Semantic value stack (contains values of tokens and semantic action results)
+        $this->semStack = array();
+
+        // Current position in the stack(s)
+        $this->stackPos = 0;
+
+        $errorState = 0;
+
+        for (;;) {
+            //$this->traceNewState($state, $symbol);
+
+            if ($this->actionBase[$state] == 0) {
+                $rule = $this->actionDefault[$state];
+            } else {
+                if ($symbol === self::SYMBOL_NONE) {
+                    // Fetch the next token id from the lexer and fetch additional info by-ref.
+                    // The end attributes are fetched into a temporary variable and only set once the token is really
+                    // shifted (not during read). Otherwise you would sometimes get off-by-one errors, when a rule is
+                    // reduced after a token was read but not yet shifted.
+                    $tokenId = $this->lexer->getNextToken($tokenValue, $startAttributes, $endAttributes);
+
+                    // map the lexer token id to the internally used symbols
+                    $symbol = $tokenId >= 0 && $tokenId < $this->tokenToSymbolMapSize
+                        ? $this->tokenToSymbol[$tokenId]
+                        : $this->invalidSymbol;
+
+                    if ($symbol === $this->invalidSymbol) {
+                        throw new \RangeException(sprintf(
+                            'The lexer returned an invalid token (id=%d, value=%s)',
+                            $tokenId, $tokenValue
+                        ));
+                    }
+
+                    // This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get
+                    // the attributes of the next token, even though they don't contain it themselves.
+                    $this->startAttributeStack[$this->stackPos+1] = $startAttributes;
+
+                    //$this->traceRead($symbol);
+                }
+
+                $idx = $this->actionBase[$state] + $symbol;
+                if ((($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $symbol)
+                     || ($state < $this->YY2TBLSTATE
+                         && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $symbol) >= 0
+                         && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $symbol))
+                    && ($action = $this->action[$idx]) != $this->defaultAction) {
+                    /*
+                     * >= YYNLSTATES: shift and reduce
+                     * > 0: shift
+                     * = 0: accept
+                     * < 0: reduce
+                     * = -YYUNEXPECTED: error
+                     */
+                    if ($action > 0) {
+                        /* shift */
+                        //$this->traceShift($symbol);
+
+                        ++$this->stackPos;
+                        $stateStack[$this->stackPos] = $state = $action;
+                        $this->semStack[$this->stackPos] = $tokenValue;
+                        $this->startAttributeStack[$this->stackPos] = $startAttributes;
+                        $this->endAttributes = $endAttributes;
+                        $symbol = self::SYMBOL_NONE;
+
+                        if ($errorState) {
+                            --$errorState;
+                        }
+
+                        if ($action < $this->YYNLSTATES) {
+                            continue;
+                        }
+
+                        /* $yyn >= YYNLSTATES means shift-and-reduce */
+                        $rule = $action - $this->YYNLSTATES;
+                    } else {
+                        $rule = -$action;
+                    }
+                } else {
+                    $rule = $this->actionDefault[$state];
+                }
+            }
+
+            for (;;) {
+                if ($rule === 0) {
+                    /* accept */
+                    //$this->traceAccept();
+                    return $this->semValue;
+                } elseif ($rule !== $this->unexpectedTokenRule) {
+                    /* reduce */
+                    //$this->traceReduce($rule);
+
+                    try {
+                        $this->{'reduceRule' . $rule}();
+                    } catch (Error $e) {
+                        if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) {
+                            $e->setStartLine($startAttributes['startLine']);
+                        }
+
+                        $this->errors[] = $e;
+                        if ($this->throwOnError) {
+                            throw $e;
+                        } else {
+                            // Currently can't recover from "special" errors
+                            return null;
+                        }
+                    }
+
+                    /* Goto - shift nonterminal */
+                    $this->stackPos -= $this->ruleToLength[$rule];
+                    $nonTerminal = $this->ruleToNonTerminal[$rule];
+                    $idx = $this->gotoBase[$nonTerminal] + $stateStack[$this->stackPos];
+                    if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] == $nonTerminal) {
+                        $state = $this->goto[$idx];
+                    } else {
+                        $state = $this->gotoDefault[$nonTerminal];
+                    }
+
+                    ++$this->stackPos;
+                    $stateStack[$this->stackPos]     = $state;
+                    $this->semStack[$this->stackPos] = $this->semValue;
+                } else {
+                    /* error */
+                    switch ($errorState) {
+                        case 0:
+                            $msg = $this->getErrorMessage($symbol, $state);
+                            $error = new Error($msg, $startAttributes + $endAttributes);
+                            $this->errors[] = $error;
+                            if ($this->throwOnError) {
+                                throw $error;
+                            }
+                            // Break missing intentionally
+                        case 1:
+                        case 2:
+                            $errorState = 3;
+
+                            // Pop until error-expecting state uncovered
+                            while (!(
+                                (($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0
+                                    && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $this->errorSymbol)
+                                || ($state < $this->YY2TBLSTATE
+                                    && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $this->errorSymbol) >= 0
+                                    && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $this->errorSymbol)
+                            ) || ($action = $this->action[$idx]) == $this->defaultAction) { // Not totally sure about this
+                                if ($this->stackPos <= 0) {
+                                    // Could not recover from error
+                                    return null;
+                                }
+                                $state = $stateStack[--$this->stackPos];
+                                //$this->tracePop($state);
+                            }
+
+                            //$this->traceShift($this->errorSymbol);
+                            $stateStack[++$this->stackPos] = $state = $action;
+                            break;
+
+                        case 3:
+                            if ($symbol === 0) {
+                                // Reached EOF without recovering from error
+                                return null;
+                            }
+
+                            //$this->traceDiscard($symbol);
+                            $symbol = self::SYMBOL_NONE;
+                            break 2;
+                    }
+                }
+
+                if ($state < $this->YYNLSTATES) {
+                    break;
+                }
+
+                /* >= YYNLSTATES means shift-and-reduce */
+                $rule = $state - $this->YYNLSTATES;
+            }
+        }
+
+        throw new \RuntimeException('Reached end of parser loop');
+    }
+
+    protected function getErrorMessage($symbol, $state) {
+        $expectedString = '';
+        if ($expected = $this->getExpectedTokens($state)) {
+            $expectedString = ', expecting ' . implode(' or ', $expected);
+        }
+
+        return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString;
+    }
+
+    protected function getExpectedTokens($state) {
+        $expected = array();
+
+        $base = $this->actionBase[$state];
+        foreach ($this->symbolToName as $symbol => $name) {
+            $idx = $base + $symbol;
+            if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
+                || $state < $this->YY2TBLSTATE
+                && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $symbol) >= 0
+                && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
+            ) {
+                if ($this->action[$idx] != $this->unexpectedTokenRule) {
+                    if (count($expected) == 4) {
+                        /* Too many expected tokens */
+                        return array();
+                    }
+
+                    $expected[] = $name;
+                }
+            }
+        }
+
+        return $expected;
+    }
+
+    /*
+     * Tracing functions used for debugging the parser.
+     */
+
+    /*
+    protected function traceNewState($state, $symbol) {
+        echo '% State ' . $state
+            . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n";
+    }
+
+    protected function traceRead($symbol) {
+        echo '% Reading ' . $this->symbolToName[$symbol] . "\n";
+    }
+
+    protected function traceShift($symbol) {
+        echo '% Shift ' . $this->symbolToName[$symbol] . "\n";
+    }
+
+    protected function traceAccept() {
+        echo "% Accepted.\n";
+    }
+
+    protected function traceReduce($n) {
+        echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n";
+    }
+
+    protected function tracePop($state) {
+        echo '% Recovering, uncovered state ' . $state . "\n";
+    }
+
+    protected function traceDiscard($symbol) {
+        echo '% Discard ' . $this->symbolToName[$symbol] . "\n";
+    }
+    */
+
+    /*
+     * Helper functions invoked by semantic actions
+     */
+
+    /**
+     * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions.
+     *
+     * @param Node[] $stmts
+     * @return Node[]
+     */
+    protected function handleNamespaces(array $stmts) {
+        $style = $this->getNamespacingStyle($stmts);
+        if (null === $style) {
+            // not namespaced, nothing to do
+            return $stmts;
+        } elseif ('brace' === $style) {
+            // For braced namespaces we only have to check that there are no invalid statements between the namespaces
+            $afterFirstNamespace = false;
+            foreach ($stmts as $stmt) {
+                if ($stmt instanceof Node\Stmt\Namespace_) {
+                    $afterFirstNamespace = true;
+                } elseif (!$stmt instanceof Node\Stmt\HaltCompiler && $afterFirstNamespace) {
+                    throw new Error('No code may exist outside of namespace {}', $stmt->getLine());
+                }
+            }
+            return $stmts;
+        } else {
+            // For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts
+            $resultStmts = array();
+            $targetStmts =& $resultStmts;
+            foreach ($stmts as $stmt) {
+                if ($stmt instanceof Node\Stmt\Namespace_) {
+                    $stmt->stmts = array();
+                    $targetStmts =& $stmt->stmts;
+                    $resultStmts[] = $stmt;
+                } elseif ($stmt instanceof Node\Stmt\HaltCompiler) {
+                    // __halt_compiler() is not moved into the namespace
+                    $resultStmts[] = $stmt;
+                } else {
+                    $targetStmts[] = $stmt;
+                }
+            }
+            return $resultStmts;
+        }
+    }
+
+    private function getNamespacingStyle(array $stmts) {
+        $style = null;
+        $hasNotAllowedStmts = false;
+        foreach ($stmts as $i => $stmt) {
+            if ($stmt instanceof Node\Stmt\Namespace_) {
+                $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace';
+                if (null === $style) {
+                    $style = $currentStyle;
+                    if ($hasNotAllowedStmts) {
+                        throw new Error('Namespace declaration statement has to be the very first statement in the script', $stmt->getLine());
+                    }
+                } elseif ($style !== $currentStyle) {
+                    throw new Error('Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $stmt->getLine());
+                }
+                continue;
+            }
+
+            /* declare() and __halt_compiler() can be used before a namespace declaration */
+            if ($stmt instanceof Node\Stmt\Declare_ || $stmt instanceof Node\Stmt\HaltCompiler) {
+                continue;
+            }
+
+            /* There may be a hashbang line at the very start of the file */
+            if ($i == 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) {
+                continue;
+            }
+
+            /* Everything else if forbidden before namespace declarations */
+            $hasNotAllowedStmts = true;
+        }
+        return $style;
+    }
+
+    protected function handleScalarTypes(Name $name) {
+        $scalarTypes = [
+            'bool'   => true,
+            'int'    => true,
+            'float'  => true,
+            'string' => true,
+        ];
+
+        if (!$name->isUnqualified()) {
+            return $name;
+        }
+
+        $lowerName = strtolower($name->toString());
+        return isset($scalarTypes[$lowerName]) ? $lowerName : $name;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/ParserFactory.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/ParserFactory.php
new file mode 100644
index 00000000000..28b9070d404
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/ParserFactory.php
@@ -0,0 +1,43 @@
+type ? $this->pType($node->type) . ' ' : '')
+             . ($node->byRef ? '&' : '')
+             . ($node->variadic ? '...' : '')
+             . '$' . $node->name
+             . ($node->default ? ' = ' . $this->p($node->default) : '');
+    }
+
+    public function pArg(Node\Arg $node) {
+        return ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value);
+    }
+
+    public function pConst(Node\Const_ $node) {
+        return $node->name . ' = ' . $this->p($node->value);
+    }
+
+    // Names
+
+    public function pName(Name $node) {
+        return implode('\\', $node->parts);
+    }
+
+    public function pName_FullyQualified(Name\FullyQualified $node) {
+        return '\\' . implode('\\', $node->parts);
+    }
+
+    public function pName_Relative(Name\Relative $node) {
+        return 'namespace\\' . implode('\\', $node->parts);
+    }
+
+    // Magic Constants
+
+    public function pScalar_MagicConst_Class(MagicConst\Class_ $node) {
+        return '__CLASS__';
+    }
+
+    public function pScalar_MagicConst_Dir(MagicConst\Dir $node) {
+        return '__DIR__';
+    }
+
+    public function pScalar_MagicConst_File(MagicConst\File $node) {
+        return '__FILE__';
+    }
+
+    public function pScalar_MagicConst_Function(MagicConst\Function_ $node) {
+        return '__FUNCTION__';
+    }
+
+    public function pScalar_MagicConst_Line(MagicConst\Line $node) {
+        return '__LINE__';
+    }
+
+    public function pScalar_MagicConst_Method(MagicConst\Method $node) {
+        return '__METHOD__';
+    }
+
+    public function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node) {
+        return '__NAMESPACE__';
+    }
+
+    public function pScalar_MagicConst_Trait(MagicConst\Trait_ $node) {
+        return '__TRAIT__';
+    }
+
+    // Scalars
+
+    public function pScalar_String(Scalar\String_ $node) {
+        return '\'' . $this->pNoIndent(addcslashes($node->value, '\'\\')) . '\'';
+    }
+
+    public function pScalar_Encapsed(Scalar\Encapsed $node) {
+        return '"' . $this->pEncapsList($node->parts, '"') . '"';
+    }
+
+    public function pScalar_LNumber(Scalar\LNumber $node) {
+        return (string) $node->value;
+    }
+
+    public function pScalar_DNumber(Scalar\DNumber $node) {
+        $stringValue = sprintf('%.16G', $node->value);
+        if ($node->value !== (double) $stringValue) {
+            $stringValue = sprintf('%.17G', $node->value);
+        }
+
+        // ensure that number is really printed as float
+        return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue;
+    }
+
+    // Assignments
+
+    public function pExpr_Assign(Expr\Assign $node) {
+        return $this->pInfixOp('Expr_Assign', $node->var, ' = ', $node->expr);
+    }
+
+    public function pExpr_AssignRef(Expr\AssignRef $node) {
+        return $this->pInfixOp('Expr_AssignRef', $node->var, ' =& ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_Plus(AssignOp\Plus $node) {
+        return $this->pInfixOp('Expr_AssignOp_Plus', $node->var, ' += ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_Minus(AssignOp\Minus $node) {
+        return $this->pInfixOp('Expr_AssignOp_Minus', $node->var, ' -= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_Mul(AssignOp\Mul $node) {
+        return $this->pInfixOp('Expr_AssignOp_Mul', $node->var, ' *= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_Div(AssignOp\Div $node) {
+        return $this->pInfixOp('Expr_AssignOp_Div', $node->var, ' /= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_Concat(AssignOp\Concat $node) {
+        return $this->pInfixOp('Expr_AssignOp_Concat', $node->var, ' .= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_Mod(AssignOp\Mod $node) {
+        return $this->pInfixOp('Expr_AssignOp_Mod', $node->var, ' %= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) {
+        return $this->pInfixOp('Expr_AssignOp_BitwiseAnd', $node->var, ' &= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) {
+        return $this->pInfixOp('Expr_AssignOp_BitwiseOr', $node->var, ' |= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) {
+        return $this->pInfixOp('Expr_AssignOp_BitwiseXor', $node->var, ' ^= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) {
+        return $this->pInfixOp('Expr_AssignOp_ShiftLeft', $node->var, ' <<= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) {
+        return $this->pInfixOp('Expr_AssignOp_ShiftRight', $node->var, ' >>= ', $node->expr);
+    }
+
+    public function pExpr_AssignOp_Pow(AssignOp\Pow $node) {
+        return $this->pInfixOp('Expr_AssignOp_Pow', $node->var, ' **= ', $node->expr);
+    }
+
+    // Binary expressions
+
+    public function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Plus', $node->left, ' + ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Minus', $node->left, ' - ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Mul', $node->left, ' * ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Div(BinaryOp\Div $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Div', $node->left, ' / ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Concat', $node->left, ' . ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Mod', $node->left, ' % ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) {
+        return $this->pInfixOp('Expr_BinaryOp_BooleanAnd', $node->left, ' && ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) {
+        return $this->pInfixOp('Expr_BinaryOp_BooleanOr', $node->left, ' || ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) {
+        return $this->pInfixOp('Expr_BinaryOp_BitwiseAnd', $node->left, ' & ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) {
+        return $this->pInfixOp('Expr_BinaryOp_BitwiseOr', $node->left, ' | ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) {
+        return $this->pInfixOp('Expr_BinaryOp_BitwiseXor', $node->left, ' ^ ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) {
+        return $this->pInfixOp('Expr_BinaryOp_ShiftLeft', $node->left, ' << ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) {
+        return $this->pInfixOp('Expr_BinaryOp_ShiftRight', $node->left, ' >> ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Pow', $node->left, ' ** ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) {
+        return $this->pInfixOp('Expr_BinaryOp_LogicalAnd', $node->left, ' and ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) {
+        return $this->pInfixOp('Expr_BinaryOp_LogicalOr', $node->left, ' or ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) {
+        return $this->pInfixOp('Expr_BinaryOp_LogicalXor', $node->left, ' xor ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Equal', $node->left, ' == ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) {
+        return $this->pInfixOp('Expr_BinaryOp_NotEqual', $node->left, ' != ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Identical', $node->left, ' === ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) {
+        return $this->pInfixOp('Expr_BinaryOp_NotIdentical', $node->left, ' !== ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Spaceship', $node->left, ' <=> ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Greater', $node->left, ' > ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) {
+        return $this->pInfixOp('Expr_BinaryOp_GreaterOrEqual', $node->left, ' >= ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Smaller', $node->left, ' < ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) {
+        return $this->pInfixOp('Expr_BinaryOp_SmallerOrEqual', $node->left, ' <= ', $node->right);
+    }
+
+    public function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) {
+        return $this->pInfixOp('Expr_BinaryOp_Coalesce', $node->left, ' ?? ', $node->right);
+    }
+
+    public function pExpr_Instanceof(Expr\Instanceof_ $node) {
+        return $this->pInfixOp('Expr_Instanceof', $node->expr, ' instanceof ', $node->class);
+    }
+
+    // Unary expressions
+
+    public function pExpr_BooleanNot(Expr\BooleanNot $node) {
+        return $this->pPrefixOp('Expr_BooleanNot', '!', $node->expr);
+    }
+
+    public function pExpr_BitwiseNot(Expr\BitwiseNot $node) {
+        return $this->pPrefixOp('Expr_BitwiseNot', '~', $node->expr);
+    }
+
+    public function pExpr_UnaryMinus(Expr\UnaryMinus $node) {
+        return $this->pPrefixOp('Expr_UnaryMinus', '-', $node->expr);
+    }
+
+    public function pExpr_UnaryPlus(Expr\UnaryPlus $node) {
+        return $this->pPrefixOp('Expr_UnaryPlus', '+', $node->expr);
+    }
+
+    public function pExpr_PreInc(Expr\PreInc $node) {
+        return $this->pPrefixOp('Expr_PreInc', '++', $node->var);
+    }
+
+    public function pExpr_PreDec(Expr\PreDec $node) {
+        return $this->pPrefixOp('Expr_PreDec', '--', $node->var);
+    }
+
+    public function pExpr_PostInc(Expr\PostInc $node) {
+        return $this->pPostfixOp('Expr_PostInc', $node->var, '++');
+    }
+
+    public function pExpr_PostDec(Expr\PostDec $node) {
+        return $this->pPostfixOp('Expr_PostDec', $node->var, '--');
+    }
+
+    public function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) {
+        return $this->pPrefixOp('Expr_ErrorSuppress', '@', $node->expr);
+    }
+
+    public function pExpr_YieldFrom(Expr\YieldFrom $node) {
+        return $this->pPrefixOp('Expr_YieldFrom', 'yield from ', $node->expr);
+    }
+
+    public function pExpr_Print(Expr\Print_ $node) {
+        return $this->pPrefixOp('Expr_Print', 'print ', $node->expr);
+    }
+
+    // Casts
+
+    public function pExpr_Cast_Int(Cast\Int_ $node) {
+        return $this->pPrefixOp('Expr_Cast_Int', '(int) ', $node->expr);
+    }
+
+    public function pExpr_Cast_Double(Cast\Double $node) {
+        return $this->pPrefixOp('Expr_Cast_Double', '(double) ', $node->expr);
+    }
+
+    public function pExpr_Cast_String(Cast\String_ $node) {
+        return $this->pPrefixOp('Expr_Cast_String', '(string) ', $node->expr);
+    }
+
+    public function pExpr_Cast_Array(Cast\Array_ $node) {
+        return $this->pPrefixOp('Expr_Cast_Array', '(array) ', $node->expr);
+    }
+
+    public function pExpr_Cast_Object(Cast\Object_ $node) {
+        return $this->pPrefixOp('Expr_Cast_Object', '(object) ', $node->expr);
+    }
+
+    public function pExpr_Cast_Bool(Cast\Bool_ $node) {
+        return $this->pPrefixOp('Expr_Cast_Bool', '(bool) ', $node->expr);
+    }
+
+    public function pExpr_Cast_Unset(Cast\Unset_ $node) {
+        return $this->pPrefixOp('Expr_Cast_Unset', '(unset) ', $node->expr);
+    }
+
+    // Function calls and similar constructs
+
+    public function pExpr_FuncCall(Expr\FuncCall $node) {
+        return $this->pCallLhs($node->name)
+             . '(' . $this->pCommaSeparated($node->args) . ')';
+    }
+
+    public function pExpr_MethodCall(Expr\MethodCall $node) {
+        return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
+             . '(' . $this->pCommaSeparated($node->args) . ')';
+    }
+
+    public function pExpr_StaticCall(Expr\StaticCall $node) {
+        return $this->pDereferenceLhs($node->class) . '::'
+             . ($node->name instanceof Expr
+                ? ($node->name instanceof Expr\Variable
+                   ? $this->p($node->name)
+                   : '{' . $this->p($node->name) . '}')
+                : $node->name)
+             . '(' . $this->pCommaSeparated($node->args) . ')';
+    }
+
+    public function pExpr_Empty(Expr\Empty_ $node) {
+        return 'empty(' . $this->p($node->expr) . ')';
+    }
+
+    public function pExpr_Isset(Expr\Isset_ $node) {
+        return 'isset(' . $this->pCommaSeparated($node->vars) . ')';
+    }
+
+    public function pExpr_Eval(Expr\Eval_ $node) {
+        return 'eval(' . $this->p($node->expr) . ')';
+    }
+
+    public function pExpr_Include(Expr\Include_ $node) {
+        static $map = array(
+            Expr\Include_::TYPE_INCLUDE      => 'include',
+            Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once',
+            Expr\Include_::TYPE_REQUIRE      => 'require',
+            Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once',
+        );
+
+        return $map[$node->type] . ' ' . $this->p($node->expr);
+    }
+
+    public function pExpr_List(Expr\List_ $node) {
+        $pList = array();
+        foreach ($node->vars as $var) {
+            if (null === $var) {
+                $pList[] = '';
+            } else {
+                $pList[] = $this->p($var);
+            }
+        }
+
+        return 'list(' . implode(', ', $pList) . ')';
+    }
+
+    // Other
+
+    public function pExpr_Variable(Expr\Variable $node) {
+        if ($node->name instanceof Expr) {
+            return '${' . $this->p($node->name) . '}';
+        } else {
+            return '$' . $node->name;
+        }
+    }
+
+    public function pExpr_Array(Expr\Array_ $node) {
+        if ($this->options['shortArraySyntax']) {
+            return '[' . $this->pCommaSeparated($node->items) . ']';
+        } else {
+            return 'array(' . $this->pCommaSeparated($node->items) . ')';
+        }
+    }
+
+    public function pExpr_ArrayItem(Expr\ArrayItem $node) {
+        return (null !== $node->key ? $this->p($node->key) . ' => ' : '')
+             . ($node->byRef ? '&' : '') . $this->p($node->value);
+    }
+
+    public function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node) {
+        return $this->pDereferenceLhs($node->var)
+             . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']';
+    }
+
+    public function pExpr_ConstFetch(Expr\ConstFetch $node) {
+        return $this->p($node->name);
+    }
+
+    public function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) {
+        return $this->p($node->class) . '::' . $node->name;
+    }
+
+    public function pExpr_PropertyFetch(Expr\PropertyFetch $node) {
+        return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name);
+    }
+
+    public function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) {
+        return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
+    }
+
+    public function pExpr_ShellExec(Expr\ShellExec $node) {
+        return '`' . $this->pEncapsList($node->parts, '`') . '`';
+    }
+
+    public function pExpr_Closure(Expr\Closure $node) {
+        return ($node->static ? 'static ' : '')
+             . 'function ' . ($node->byRef ? '&' : '')
+             . '(' . $this->pCommaSeparated($node->params) . ')'
+             . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')': '')
+             . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
+             . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pExpr_ClosureUse(Expr\ClosureUse $node) {
+        return ($node->byRef ? '&' : '') . '$' . $node->var;
+    }
+
+    public function pExpr_New(Expr\New_ $node) {
+        if ($node->class instanceof Stmt\Class_) {
+            $args = $node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : '';
+            return 'new ' . $this->pClassCommon($node->class, $args);
+        }
+        return 'new ' . $this->p($node->class) . '(' . $this->pCommaSeparated($node->args) . ')';
+    }
+
+    public function pExpr_Clone(Expr\Clone_ $node) {
+        return 'clone ' . $this->p($node->expr);
+    }
+
+    public function pExpr_Ternary(Expr\Ternary $node) {
+        // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator.
+        // this is okay because the part between ? and : never needs parentheses.
+        return $this->pInfixOp('Expr_Ternary',
+            $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else
+        );
+    }
+
+    public function pExpr_Exit(Expr\Exit_ $node) {
+        return 'die' . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
+    }
+
+    public function pExpr_Yield(Expr\Yield_ $node) {
+        if ($node->value === null) {
+            return 'yield';
+        } else {
+            // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary
+            return '(yield '
+                 . ($node->key !== null ? $this->p($node->key) . ' => ' : '')
+                 . $this->p($node->value)
+                 . ')';
+        }
+    }
+
+    // Declarations
+
+    public function pStmt_Namespace(Stmt\Namespace_ $node) {
+        if ($this->canUseSemicolonNamespaces) {
+            return 'namespace ' . $this->p($node->name) . ';' . "\n" . $this->pStmts($node->stmts, false);
+        } else {
+            return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
+                 . ' {' . $this->pStmts($node->stmts) . "\n" . '}';
+        }
+    }
+
+    public function pStmt_Use(Stmt\Use_ $node) {
+        return 'use ' . $this->pUseType($node->type)
+             . $this->pCommaSeparated($node->uses) . ';';
+    }
+
+    public function pStmt_GroupUse(Stmt\GroupUse $node) {
+        return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
+             . '\{' . $this->pCommaSeparated($node->uses) . '};';
+    }
+
+    public function pStmt_UseUse(Stmt\UseUse $node) {
+        return $this->pUseType($node->type) . $this->p($node->name)
+             . ($node->name->getLast() !== $node->alias ? ' as ' . $node->alias : '');
+    }
+
+    private function pUseType($type) {
+        return $type === Stmt\Use_::TYPE_FUNCTION ? 'function '
+            : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '');
+    }
+
+    public function pStmt_Interface(Stmt\Interface_ $node) {
+        return 'interface ' . $node->name
+             . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
+             . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_Class(Stmt\Class_ $node) {
+        return $this->pClassCommon($node, ' ' . $node->name);
+    }
+
+    public function pStmt_Trait(Stmt\Trait_ $node) {
+        return 'trait ' . $node->name
+             . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_TraitUse(Stmt\TraitUse $node) {
+        return 'use ' . $this->pCommaSeparated($node->traits)
+             . (empty($node->adaptations)
+                ? ';'
+                : ' {' . $this->pStmts($node->adaptations) . "\n" . '}');
+    }
+
+    public function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) {
+        return $this->p($node->trait) . '::' . $node->method
+             . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';';
+    }
+
+    public function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node) {
+        return (null !== $node->trait ? $this->p($node->trait) . '::' : '')
+             . $node->method . ' as'
+             . (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '')
+             . (null !== $node->newName     ? ' ' . $node->newName                        : '')
+             . ';';
+    }
+
+    public function pStmt_Property(Stmt\Property $node) {
+        return (0 === $node->type ? 'var ' : $this->pModifiers($node->type)) . $this->pCommaSeparated($node->props) . ';';
+    }
+
+    public function pStmt_PropertyProperty(Stmt\PropertyProperty $node) {
+        return '$' . $node->name
+             . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
+    }
+
+    public function pStmt_ClassMethod(Stmt\ClassMethod $node) {
+        return $this->pModifiers($node->type)
+             . 'function ' . ($node->byRef ? '&' : '') . $node->name
+             . '(' . $this->pCommaSeparated($node->params) . ')'
+             . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
+             . (null !== $node->stmts
+                ? "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}'
+                : ';');
+    }
+
+    public function pStmt_ClassConst(Stmt\ClassConst $node) {
+        return 'const ' . $this->pCommaSeparated($node->consts) . ';';
+    }
+
+    public function pStmt_Function(Stmt\Function_ $node) {
+        return 'function ' . ($node->byRef ? '&' : '') . $node->name
+             . '(' . $this->pCommaSeparated($node->params) . ')'
+             . (null !== $node->returnType ? ' : ' . $this->pType($node->returnType) : '')
+             . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_Const(Stmt\Const_ $node) {
+        return 'const ' . $this->pCommaSeparated($node->consts) . ';';
+    }
+
+    public function pStmt_Declare(Stmt\Declare_ $node) {
+        return 'declare (' . $this->pCommaSeparated($node->declares) . ')'
+             . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . "\n" . '}' : ';');
+    }
+
+    public function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) {
+        return $node->key . '=' . $this->p($node->value);
+    }
+
+    // Control flow
+
+    public function pStmt_If(Stmt\If_ $node) {
+        return 'if (' . $this->p($node->cond) . ') {'
+             . $this->pStmts($node->stmts) . "\n" . '}'
+             . $this->pImplode($node->elseifs)
+             . (null !== $node->else ? $this->p($node->else) : '');
+    }
+
+    public function pStmt_ElseIf(Stmt\ElseIf_ $node) {
+        return ' elseif (' . $this->p($node->cond) . ') {'
+             . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_Else(Stmt\Else_ $node) {
+        return ' else {' . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_For(Stmt\For_ $node) {
+        return 'for ('
+             . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
+             . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
+             . $this->pCommaSeparated($node->loop)
+             . ') {' . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_Foreach(Stmt\Foreach_ $node) {
+        return 'foreach (' . $this->p($node->expr) . ' as '
+             . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
+             . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
+             . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_While(Stmt\While_ $node) {
+        return 'while (' . $this->p($node->cond) . ') {'
+             . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_Do(Stmt\Do_ $node) {
+        return 'do {' . $this->pStmts($node->stmts) . "\n"
+             . '} while (' . $this->p($node->cond) . ');';
+    }
+
+    public function pStmt_Switch(Stmt\Switch_ $node) {
+        return 'switch (' . $this->p($node->cond) . ') {'
+             . $this->pStmts($node->cases) . "\n" . '}';
+    }
+
+    public function pStmt_Case(Stmt\Case_ $node) {
+        return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
+             . $this->pStmts($node->stmts);
+    }
+
+    public function pStmt_TryCatch(Stmt\TryCatch $node) {
+        return 'try {' . $this->pStmts($node->stmts) . "\n" . '}'
+             . $this->pImplode($node->catches)
+             . ($node->finallyStmts !== null
+                ? ' finally {' . $this->pStmts($node->finallyStmts) . "\n" . '}'
+                : '');
+    }
+
+    public function pStmt_Catch(Stmt\Catch_ $node) {
+        return ' catch (' . $this->p($node->type) . ' $' . $node->var . ') {'
+             . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    public function pStmt_Break(Stmt\Break_ $node) {
+        return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
+    }
+
+    public function pStmt_Continue(Stmt\Continue_ $node) {
+        return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
+    }
+
+    public function pStmt_Return(Stmt\Return_ $node) {
+        return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';';
+    }
+
+    public function pStmt_Throw(Stmt\Throw_ $node) {
+        return 'throw ' . $this->p($node->expr) . ';';
+    }
+
+    public function pStmt_Label(Stmt\Label $node) {
+        return $node->name . ':';
+    }
+
+    public function pStmt_Goto(Stmt\Goto_ $node) {
+        return 'goto ' . $node->name . ';';
+    }
+
+    // Other
+
+    public function pStmt_Echo(Stmt\Echo_ $node) {
+        return 'echo ' . $this->pCommaSeparated($node->exprs) . ';';
+    }
+
+    public function pStmt_Static(Stmt\Static_ $node) {
+        return 'static ' . $this->pCommaSeparated($node->vars) . ';';
+    }
+
+    public function pStmt_Global(Stmt\Global_ $node) {
+        return 'global ' . $this->pCommaSeparated($node->vars) . ';';
+    }
+
+    public function pStmt_StaticVar(Stmt\StaticVar $node) {
+        return '$' . $node->name
+             . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
+    }
+
+    public function pStmt_Unset(Stmt\Unset_ $node) {
+        return 'unset(' . $this->pCommaSeparated($node->vars) . ');';
+    }
+
+    public function pStmt_InlineHTML(Stmt\InlineHTML $node) {
+        return '?>' . $this->pNoIndent("\n" . $node->value) . 'remaining;
+    }
+
+    // Helpers
+
+    protected function pType($node) {
+        return is_string($node) ? $node : $this->p($node);
+    }
+
+    protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) {
+        return $this->pModifiers($node->type)
+        . 'class' . $afterClassToken
+        . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
+        . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
+        . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
+    }
+
+    protected function pObjectProperty($node) {
+        if ($node instanceof Expr) {
+            return '{' . $this->p($node) . '}';
+        } else {
+            return $node;
+        }
+    }
+
+    protected function pModifiers($modifiers) {
+        return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC    ? 'public '    : '')
+             . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '')
+             . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE   ? 'private '   : '')
+             . ($modifiers & Stmt\Class_::MODIFIER_STATIC    ? 'static '    : '')
+             . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT  ? 'abstract '  : '')
+             . ($modifiers & Stmt\Class_::MODIFIER_FINAL     ? 'final '     : '');
+    }
+
+    protected function pEncapsList(array $encapsList, $quote) {
+        $return = '';
+        foreach ($encapsList as $element) {
+            if ($element instanceof Scalar\EncapsedStringPart) {
+                $return .= addcslashes($element->value, "\n\r\t\f\v$" . $quote . "\\");
+            } else {
+                $return .= '{' . $this->p($element) . '}';
+            }
+        }
+
+        return $return;
+    }
+
+    protected function pDereferenceLhs(Node $node) {
+        if ($node instanceof Expr\Variable
+            || $node instanceof Name
+            || $node instanceof Expr\ArrayDimFetch
+            || $node instanceof Expr\PropertyFetch
+            || $node instanceof Expr\StaticPropertyFetch
+            || $node instanceof Expr\FuncCall
+            || $node instanceof Expr\MethodCall
+            || $node instanceof Expr\StaticCall
+            || $node instanceof Expr\Array_
+            || $node instanceof Scalar\String_
+            || $node instanceof Expr\ConstFetch
+            || $node instanceof Expr\ClassConstFetch
+        ) {
+            return $this->p($node);
+        } else  {
+            return '(' . $this->p($node) . ')';
+        }
+    }
+
+    protected function pCallLhs(Node $node) {
+        if ($node instanceof Name
+            || $node instanceof Expr\Variable
+            || $node instanceof Expr\ArrayDimFetch
+            || $node instanceof Expr\FuncCall
+            || $node instanceof Expr\MethodCall
+            || $node instanceof Expr\StaticCall
+            || $node instanceof Expr\Array_
+        ) {
+            return $this->p($node);
+        } else  {
+            return '(' . $this->p($node) . ')';
+        }
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/PrettyPrinterAbstract.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/PrettyPrinterAbstract.php
new file mode 100644
index 00000000000..11aadd1db38
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/PrettyPrinterAbstract.php
@@ -0,0 +1,293 @@
+ array(  0,  1),
+        'Expr_BitwiseNot'              => array( 10,  1),
+        'Expr_PreInc'                  => array( 10,  1),
+        'Expr_PreDec'                  => array( 10,  1),
+        'Expr_PostInc'                 => array( 10, -1),
+        'Expr_PostDec'                 => array( 10, -1),
+        'Expr_UnaryPlus'               => array( 10,  1),
+        'Expr_UnaryMinus'              => array( 10,  1),
+        'Expr_Cast_Int'                => array( 10,  1),
+        'Expr_Cast_Double'             => array( 10,  1),
+        'Expr_Cast_String'             => array( 10,  1),
+        'Expr_Cast_Array'              => array( 10,  1),
+        'Expr_Cast_Object'             => array( 10,  1),
+        'Expr_Cast_Bool'               => array( 10,  1),
+        'Expr_Cast_Unset'              => array( 10,  1),
+        'Expr_ErrorSuppress'           => array( 10,  1),
+        'Expr_Instanceof'              => array( 20,  0),
+        'Expr_BooleanNot'              => array( 30,  1),
+        'Expr_BinaryOp_Mul'            => array( 40, -1),
+        'Expr_BinaryOp_Div'            => array( 40, -1),
+        'Expr_BinaryOp_Mod'            => array( 40, -1),
+        'Expr_BinaryOp_Plus'           => array( 50, -1),
+        'Expr_BinaryOp_Minus'          => array( 50, -1),
+        'Expr_BinaryOp_Concat'         => array( 50, -1),
+        'Expr_BinaryOp_ShiftLeft'      => array( 60, -1),
+        'Expr_BinaryOp_ShiftRight'     => array( 60, -1),
+        'Expr_BinaryOp_Smaller'        => array( 70,  0),
+        'Expr_BinaryOp_SmallerOrEqual' => array( 70,  0),
+        'Expr_BinaryOp_Greater'        => array( 70,  0),
+        'Expr_BinaryOp_GreaterOrEqual' => array( 70,  0),
+        'Expr_BinaryOp_Equal'          => array( 80,  0),
+        'Expr_BinaryOp_NotEqual'       => array( 80,  0),
+        'Expr_BinaryOp_Identical'      => array( 80,  0),
+        'Expr_BinaryOp_NotIdentical'   => array( 80,  0),
+        'Expr_BinaryOp_Spaceship'      => array( 80,  0),
+        'Expr_BinaryOp_BitwiseAnd'     => array( 90, -1),
+        'Expr_BinaryOp_BitwiseXor'     => array(100, -1),
+        'Expr_BinaryOp_BitwiseOr'      => array(110, -1),
+        'Expr_BinaryOp_BooleanAnd'     => array(120, -1),
+        'Expr_BinaryOp_BooleanOr'      => array(130, -1),
+        'Expr_BinaryOp_Coalesce'       => array(140,  1),
+        'Expr_Ternary'                 => array(150, -1),
+        // parser uses %left for assignments, but they really behave as %right
+        'Expr_Assign'                  => array(160,  1),
+        'Expr_AssignRef'               => array(160,  1),
+        'Expr_AssignOp_Plus'           => array(160,  1),
+        'Expr_AssignOp_Minus'          => array(160,  1),
+        'Expr_AssignOp_Mul'            => array(160,  1),
+        'Expr_AssignOp_Div'            => array(160,  1),
+        'Expr_AssignOp_Concat'         => array(160,  1),
+        'Expr_AssignOp_Mod'            => array(160,  1),
+        'Expr_AssignOp_BitwiseAnd'     => array(160,  1),
+        'Expr_AssignOp_BitwiseOr'      => array(160,  1),
+        'Expr_AssignOp_BitwiseXor'     => array(160,  1),
+        'Expr_AssignOp_ShiftLeft'      => array(160,  1),
+        'Expr_AssignOp_ShiftRight'     => array(160,  1),
+        'Expr_AssignOp_Pow'            => array(160,  1),
+        'Expr_YieldFrom'               => array(165,  1),
+        'Expr_Print'                   => array(168,  1),
+        'Expr_BinaryOp_LogicalAnd'     => array(170, -1),
+        'Expr_BinaryOp_LogicalXor'     => array(180, -1),
+        'Expr_BinaryOp_LogicalOr'      => array(190, -1),
+        'Expr_Include'                 => array(200, -1),
+    );
+
+    protected $noIndentToken;
+    protected $canUseSemicolonNamespaces;
+    protected $options;
+
+    /**
+     * Creates a pretty printer instance using the given options.
+     *
+     * Supported options:
+     *  * bool $shortArraySyntax = false: Whether to use [] instead of array()
+     *
+     * @param array $options Dictionary of formatting options
+     */
+    public function __construct(array $options = []) {
+        $this->noIndentToken = '_NO_INDENT_' . mt_rand();
+
+        $defaultOptions = ['shortArraySyntax' => false];
+        $this->options = $options + $defaultOptions;
+    }
+
+    /**
+     * Pretty prints an array of statements.
+     *
+     * @param Node[] $stmts Array of statements
+     *
+     * @return string Pretty printed statements
+     */
+    public function prettyPrint(array $stmts) {
+        $this->preprocessNodes($stmts);
+
+        return ltrim(str_replace("\n" . $this->noIndentToken, "\n", $this->pStmts($stmts, false)));
+    }
+
+    /**
+     * Pretty prints an expression.
+     *
+     * @param Expr $node Expression node
+     *
+     * @return string Pretty printed node
+     */
+    public function prettyPrintExpr(Expr $node) {
+        return str_replace("\n" . $this->noIndentToken, "\n", $this->p($node));
+    }
+
+    /**
+     * Pretty prints a file of statements (includes the opening prettyPrint($stmts);
+
+        if ($stmts[0] instanceof Stmt\InlineHTML) {
+            $p = preg_replace('/^<\?php\s+\?>\n?/', '', $p);
+        }
+        if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) {
+            $p = preg_replace('/<\?php$/', '', rtrim($p));
+        }
+
+        return $p;
+    }
+
+    /**
+     * Preprocesses the top-level nodes to initialize pretty printer state.
+     *
+     * @param Node[] $nodes Array of nodes
+     */
+    protected function preprocessNodes(array $nodes) {
+        /* We can use semicolon-namespaces unless there is a global namespace declaration */
+        $this->canUseSemicolonNamespaces = true;
+        foreach ($nodes as $node) {
+            if ($node instanceof Stmt\Namespace_ && null === $node->name) {
+                $this->canUseSemicolonNamespaces = false;
+            }
+        }
+    }
+
+    /**
+     * Pretty prints an array of nodes (statements) and indents them optionally.
+     *
+     * @param Node[] $nodes  Array of nodes
+     * @param bool   $indent Whether to indent the printed nodes
+     *
+     * @return string Pretty printed statements
+     */
+    protected function pStmts(array $nodes, $indent = true) {
+        $result = '';
+        foreach ($nodes as $node) {
+            $result .= "\n"
+                    . $this->pComments($node->getAttribute('comments', array()))
+                    . $this->p($node)
+                    . ($node instanceof Expr ? ';' : '');
+        }
+
+        if ($indent) {
+            return preg_replace('~\n(?!$|' . $this->noIndentToken . ')~', "\n    ", $result);
+        } else {
+            return $result;
+        }
+    }
+
+    /**
+     * Pretty prints a node.
+     *
+     * @param Node $node Node to be pretty printed
+     *
+     * @return string Pretty printed node
+     */
+    protected function p(Node $node) {
+        return $this->{'p' . $node->getType()}($node);
+    }
+
+    protected function pInfixOp($type, Node $leftNode, $operatorString, Node $rightNode) {
+        list($precedence, $associativity) = $this->precedenceMap[$type];
+
+        return $this->pPrec($leftNode, $precedence, $associativity, -1)
+             . $operatorString
+             . $this->pPrec($rightNode, $precedence, $associativity, 1);
+    }
+
+    protected function pPrefixOp($type, $operatorString, Node $node) {
+        list($precedence, $associativity) = $this->precedenceMap[$type];
+        return $operatorString . $this->pPrec($node, $precedence, $associativity, 1);
+    }
+
+    protected function pPostfixOp($type, Node $node, $operatorString) {
+        list($precedence, $associativity) = $this->precedenceMap[$type];
+        return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString;
+    }
+
+    /**
+     * Prints an expression node with the least amount of parentheses necessary to preserve the meaning.
+     *
+     * @param Node $node                Node to pretty print
+     * @param int  $parentPrecedence    Precedence of the parent operator
+     * @param int  $parentAssociativity Associativity of parent operator
+     *                                  (-1 is left, 0 is nonassoc, 1 is right)
+     * @param int  $childPosition       Position of the node relative to the operator
+     *                                  (-1 is left, 1 is right)
+     *
+     * @return string The pretty printed node
+     */
+    protected function pPrec(Node $node, $parentPrecedence, $parentAssociativity, $childPosition) {
+        $type = $node->getType();
+        if (isset($this->precedenceMap[$type])) {
+            $childPrecedence = $this->precedenceMap[$type][0];
+            if ($childPrecedence > $parentPrecedence
+                || ($parentPrecedence == $childPrecedence && $parentAssociativity != $childPosition)
+            ) {
+                return '(' . $this->{'p' . $type}($node) . ')';
+            }
+        }
+
+        return $this->{'p' . $type}($node);
+    }
+
+    /**
+     * Pretty prints an array of nodes and implodes the printed values.
+     *
+     * @param Node[] $nodes Array of Nodes to be printed
+     * @param string $glue  Character to implode with
+     *
+     * @return string Imploded pretty printed nodes
+     */
+    protected function pImplode(array $nodes, $glue = '') {
+        $pNodes = array();
+        foreach ($nodes as $node) {
+            $pNodes[] = $this->p($node);
+        }
+
+        return implode($glue, $pNodes);
+    }
+
+    /**
+     * Pretty prints an array of nodes and implodes the printed values with commas.
+     *
+     * @param Node[] $nodes Array of Nodes to be printed
+     *
+     * @return string Comma separated pretty printed nodes
+     */
+    protected function pCommaSeparated(array $nodes) {
+        return $this->pImplode($nodes, ', ');
+    }
+
+    /**
+     * Signals the pretty printer that a string shall not be indented.
+     *
+     * @param string $string Not to be indented string
+     *
+     * @return string String marked with $this->noIndentToken's.
+     */
+    protected function pNoIndent($string) {
+        return str_replace("\n", "\n" . $this->noIndentToken, $string);
+    }
+
+    /**
+     * Prints reformatted text of the passed comments.
+     *
+     * @param Comment[] $comments List of comments
+     *
+     * @return string Reformatted text of comments
+     */
+    protected function pComments(array $comments) {
+        $result = '';
+
+        foreach ($comments as $comment) {
+            $result .= $comment->getReformattedText() . "\n";
+        }
+
+        return $result;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Serializer.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Serializer.php
new file mode 100644
index 00000000000..7c173cda753
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Serializer.php
@@ -0,0 +1,15 @@
+writer = new XMLWriter;
+        $this->writer->openMemory();
+        $this->writer->setIndent(true);
+    }
+
+    public function serialize(array $nodes) {
+        $this->writer->flush();
+        $this->writer->startDocument('1.0', 'UTF-8');
+
+        $this->writer->startElement('AST');
+        $this->writer->writeAttribute('xmlns:node',      'http://nikic.github.com/PHPParser/XML/node');
+        $this->writer->writeAttribute('xmlns:subNode',   'http://nikic.github.com/PHPParser/XML/subNode');
+        $this->writer->writeAttribute('xmlns:attribute', 'http://nikic.github.com/PHPParser/XML/attribute');
+        $this->writer->writeAttribute('xmlns:scalar',    'http://nikic.github.com/PHPParser/XML/scalar');
+
+        $this->_serialize($nodes);
+
+        $this->writer->endElement();
+
+        return $this->writer->outputMemory();
+    }
+
+    protected function _serialize($node) {
+        if ($node instanceof Node) {
+            $this->writer->startElement('node:' . $node->getType());
+
+            foreach ($node->getAttributes() as $name => $value) {
+                $this->writer->startElement('attribute:' . $name);
+                $this->_serialize($value);
+                $this->writer->endElement();
+            }
+
+            foreach ($node as $name => $subNode) {
+                $this->writer->startElement('subNode:' . $name);
+                $this->_serialize($subNode);
+                $this->writer->endElement();
+            }
+
+            $this->writer->endElement();
+        } elseif ($node instanceof Comment) {
+            $this->writer->startElement('comment');
+            $this->writer->writeAttribute('isDocComment', $node instanceof Comment\Doc ? 'true' : 'false');
+            $this->writer->writeAttribute('line', (string) $node->getLine());
+            $this->writer->text($node->getText());
+            $this->writer->endElement();
+        } elseif (is_array($node)) {
+            $this->writer->startElement('scalar:array');
+            foreach ($node as $subNode) {
+                $this->_serialize($subNode);
+            }
+            $this->writer->endElement();
+        } elseif (is_string($node)) {
+            $this->writer->writeElement('scalar:string', $node);
+        } elseif (is_int($node)) {
+            $this->writer->writeElement('scalar:int', (string) $node);
+        } elseif (is_float($node)) {
+            // TODO Higher precision conversion?
+            $this->writer->writeElement('scalar:float', (string) $node);
+        } elseif (true === $node) {
+            $this->writer->writeElement('scalar:true');
+        } elseif (false === $node) {
+            $this->writer->writeElement('scalar:false');
+        } elseif (null === $node) {
+            $this->writer->writeElement('scalar:null');
+        } else {
+            throw new \InvalidArgumentException('Unexpected node type');
+        }
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Unserializer.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Unserializer.php
new file mode 100644
index 00000000000..bfa6da0c43d
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/PhpParser/Unserializer.php
@@ -0,0 +1,15 @@
+reader = new XMLReader;
+    }
+
+    public function unserialize($string) {
+        $this->reader->XML($string);
+
+        $this->reader->read();
+        if ('AST' !== $this->reader->name) {
+            throw new DomainException('AST root element not found');
+        }
+
+        return $this->read($this->reader->depth);
+    }
+
+    protected function read($depthLimit, $throw = true, &$nodeFound = null) {
+        $nodeFound = true;
+        while ($this->reader->read() && $depthLimit < $this->reader->depth) {
+            if (XMLReader::ELEMENT !== $this->reader->nodeType) {
+                continue;
+            }
+
+            if ('node' === $this->reader->prefix) {
+                return $this->readNode();
+            } elseif ('scalar' === $this->reader->prefix) {
+                return $this->readScalar();
+            } elseif ('comment' === $this->reader->name) {
+                return $this->readComment();
+            } else {
+                throw new DomainException(sprintf('Unexpected node of type "%s"', $this->reader->name));
+            }
+        }
+
+        $nodeFound = false;
+        if ($throw) {
+            throw new DomainException('Expected node or scalar');
+        }
+    }
+
+    protected function readNode() {
+        $className = $this->getClassNameFromType($this->reader->localName);
+
+        // create the node without calling it's constructor
+        $node = unserialize(
+            sprintf(
+                "O:%d:\"%s\":1:{s:13:\"\0*\0attributes\";a:0:{}}",
+                strlen($className), $className
+            )
+        );
+
+        $depthLimit = $this->reader->depth;
+        while ($this->reader->read() && $depthLimit < $this->reader->depth) {
+            if (XMLReader::ELEMENT !== $this->reader->nodeType) {
+                continue;
+            }
+
+            $type = $this->reader->prefix;
+            if ('subNode' !== $type && 'attribute' !== $type) {
+                throw new DomainException(
+                    sprintf('Expected sub node or attribute, got node of type "%s"', $this->reader->name)
+                );
+            }
+
+            $name = $this->reader->localName;
+            $value = $this->read($this->reader->depth);
+
+            if ('subNode' === $type) {
+                $node->$name = $value;
+            } else {
+                $node->setAttribute($name, $value);
+            }
+        }
+
+        return $node;
+    }
+
+    protected function readScalar() {
+        switch ($name = $this->reader->localName) {
+            case 'array':
+                $depth = $this->reader->depth;
+                $array = array();
+                while (true) {
+                    $node = $this->read($depth, false, $nodeFound);
+                    if (!$nodeFound) {
+                        break;
+                    }
+                    $array[] = $node;
+                }
+                return $array;
+            case 'string':
+                return $this->reader->readString();
+            case 'int':
+                return $this->parseInt($this->reader->readString());
+            case 'float':
+                $text = $this->reader->readString();
+                if (false === $float = filter_var($text, FILTER_VALIDATE_FLOAT)) {
+                    throw new DomainException(sprintf('"%s" is not a valid float', $text));
+                }
+                return $float;
+            case 'true':
+            case 'false':
+            case 'null':
+                if (!$this->reader->isEmptyElement) {
+                    throw new DomainException(sprintf('"%s" scalar must be empty', $name));
+                }
+                return constant($name);
+            default:
+                throw new DomainException(sprintf('Unknown scalar type "%s"', $name));
+        }
+    }
+
+    private function parseInt($text) {
+        if (false === $int = filter_var($text, FILTER_VALIDATE_INT)) {
+            throw new DomainException(sprintf('"%s" is not a valid integer', $text));
+        }
+        return $int;
+    }
+
+    protected function readComment() {
+        $className = $this->reader->getAttribute('isDocComment') === 'true'
+            ? 'PhpParser\Comment\Doc'
+            : 'PhpParser\Comment'
+        ;
+        return new $className(
+            $this->reader->readString(),
+            $this->parseInt($this->reader->getAttribute('line'))
+        );
+    }
+
+    protected function getClassNameFromType($type) {
+        $className = 'PhpParser\\Node\\' . strtr($type, '_', '\\');
+        if (!class_exists($className)) {
+            $className .= '_';
+        }
+        if (!class_exists($className)) {
+            throw new DomainException(sprintf('Unknown node type "%s"', $type));
+        }
+        return $className;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/bootstrap.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/bootstrap.php
new file mode 100644
index 00000000000..b0f517822c1
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/lib/bootstrap.php
@@ -0,0 +1,6 @@
+
+
+
+    
+        
+            ./test/
+        
+    
+
+    
+        
+            ./lib/PhpParser/
+        
+    
+
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/AutoloaderTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/AutoloaderTest.php
new file mode 100644
index 00000000000..e16b9f2185b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/AutoloaderTest.php
@@ -0,0 +1,18 @@
+assertTrue(class_exists('PhpParser\NodeVisitorAbstract'));
+        $this->assertFalse(class_exists('PHPParser_NodeVisitor_NameResolver'));
+
+        $this->assertFalse(class_exists('PhpParser\FooBar'));
+        $this->assertFalse(class_exists('PHPParser_FooBar'));
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/ClassTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/ClassTest.php
new file mode 100644
index 00000000000..94ce67afdcb
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/ClassTest.php
@@ -0,0 +1,161 @@
+createClassBuilder('SomeLogger')
+            ->extend('BaseLogger')
+            ->implement('Namespaced\Logger', new Name('SomeInterface'))
+            ->implement('\Fully\Qualified', 'namespace\NamespaceRelative')
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Class_('SomeLogger', array(
+                'extends' => new Name('BaseLogger'),
+                'implements' => array(
+                    new Name('Namespaced\Logger'),
+                    new Name('SomeInterface'),
+                    new Name\FullyQualified('Fully\Qualified'),
+                    new Name\Relative('NamespaceRelative'),
+                ),
+            )),
+            $node
+        );
+    }
+
+    public function testAbstract() {
+        $node = $this->createClassBuilder('Test')
+            ->makeAbstract()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Class_('Test', array(
+                'type' => Stmt\Class_::MODIFIER_ABSTRACT
+            )),
+            $node
+        );
+    }
+
+    public function testFinal() {
+        $node = $this->createClassBuilder('Test')
+            ->makeFinal()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Class_('Test', array(
+                'type' => Stmt\Class_::MODIFIER_FINAL
+            )),
+            $node
+        );
+    }
+
+    public function testStatementOrder() {
+        $method = new Stmt\ClassMethod('testMethod');
+        $property = new Stmt\Property(
+            Stmt\Class_::MODIFIER_PUBLIC,
+            array(new Stmt\PropertyProperty('testProperty'))
+        );
+        $const = new Stmt\ClassConst(array(
+            new Node\Const_('TEST_CONST', new Node\Scalar\String_('ABC'))
+        ));
+        $use = new Stmt\TraitUse(array(new Name('SomeTrait')));
+
+        $node = $this->createClassBuilder('Test')
+            ->addStmt($method)
+            ->addStmt($property)
+            ->addStmts(array($const, $use))
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Class_('Test', array(
+                'stmts' => array($use, $const, $property, $method)
+            )),
+            $node
+        );
+    }
+
+    public function testDocComment() {
+        $docComment = <<<'DOC'
+/**
+ * Test
+ */
+DOC;
+        $class = $this->createClassBuilder('Test')
+            ->setDocComment($docComment)
+            ->getNode();
+
+        $this->assertEquals(
+            new Stmt\Class_('Test', array(), array(
+                'comments' => array(
+                    new Comment\Doc($docComment)
+                )
+            )),
+            $class
+        );
+
+        $class = $this->createClassBuilder('Test')
+            ->setDocComment(new Comment\Doc($docComment))
+            ->getNode();
+
+        $this->assertEquals(
+            new Stmt\Class_('Test', array(), array(
+                'comments' => array(
+                    new Comment\Doc($docComment)
+                )
+            )),
+            $class
+        );
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Unexpected node of type "Stmt_Echo"
+     */
+    public function testInvalidStmtError() {
+        $this->createClassBuilder('Test')
+            ->addStmt(new Stmt\Echo_(array()))
+        ;
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Doc comment must be a string or an instance of PhpParser\Comment\Doc
+     */
+    public function testInvalidDocComment() {
+        $this->createClassBuilder('Test')
+            ->setDocComment(new Comment('Test'));
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Name cannot be empty
+     */
+    public function testEmptyName() {
+        $this->createClassBuilder('Test')
+            ->extend('');
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Name must be a string or an instance of PhpParser\Node\Name
+     */
+    public function testInvalidName() {
+        $this->createClassBuilder('Test')
+            ->extend(array('Foo'));
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/FunctionTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/FunctionTest.php
new file mode 100644
index 00000000000..88fb2052958
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/FunctionTest.php
@@ -0,0 +1,88 @@
+createFunctionBuilder('test')
+            ->makeReturnByRef()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Function_('test', array(
+                'byRef' => true
+            )),
+            $node
+        );
+    }
+
+    public function testParams() {
+        $param1 = new Node\Param('test1');
+        $param2 = new Node\Param('test2');
+        $param3 = new Node\Param('test3');
+
+        $node = $this->createFunctionBuilder('test')
+            ->addParam($param1)
+            ->addParams(array($param2, $param3))
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Function_('test', array(
+                'params' => array($param1, $param2, $param3)
+            )),
+            $node
+        );
+    }
+
+    public function testStmts() {
+        $stmt1 = new Print_(new String_('test1'));
+        $stmt2 = new Print_(new String_('test2'));
+        $stmt3 = new Print_(new String_('test3'));
+
+        $node = $this->createFunctionBuilder('test')
+            ->addStmt($stmt1)
+            ->addStmts(array($stmt2, $stmt3))
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Function_('test', array(
+                'stmts' => array($stmt1, $stmt2, $stmt3)
+            )),
+            $node
+        );
+    }
+
+    public function testDocComment() {
+        $node = $this->createFunctionBuilder('test')
+            ->setDocComment('/** Test */')
+            ->getNode();
+
+        $this->assertEquals(new Stmt\Function_('test', array(), array(
+            'comments' => array(new Comment\Doc('/** Test */'))
+        )), $node);
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Expected parameter node, got "Name"
+     */
+    public function testInvalidParamError() {
+        $this->createFunctionBuilder('test')
+            ->addParam(new Node\Name('foo'))
+        ;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/InterfaceTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/InterfaceTest.php
new file mode 100644
index 00000000000..c0d2fe372f3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/InterfaceTest.php
@@ -0,0 +1,105 @@
+builder = new Interface_('Contract');
+    }
+
+    private function dump($node) {
+        $pp = new \PhpParser\PrettyPrinter\Standard;
+        return $pp->prettyPrint(array($node));
+    }
+
+    public function testEmpty() {
+        $contract = $this->builder->getNode();
+        $this->assertInstanceOf('PhpParser\Node\Stmt\Interface_', $contract);
+        $this->assertSame('Contract', $contract->name);
+    }
+
+    public function testExtending() {
+        $contract = $this->builder->extend('Space\Root1', 'Root2')->getNode();
+        $this->assertEquals(
+            new Stmt\Interface_('Contract', array(
+                'extends' => array(
+                    new Node\Name('Space\Root1'),
+                    new Node\Name('Root2')
+                ),
+            )), $contract
+        );
+    }
+
+    public function testAddMethod() {
+        $method = new Stmt\ClassMethod('doSomething');
+        $contract = $this->builder->addStmt($method)->getNode();
+        $this->assertSame(array($method), $contract->stmts);
+    }
+
+    public function testAddConst() {
+        $const = new Stmt\ClassConst(array(
+            new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458.0))
+        ));
+        $contract = $this->builder->addStmt($const)->getNode();
+        $this->assertSame(299792458.0, $contract->stmts[0]->consts[0]->value->value);
+    }
+
+    public function testOrder() {
+        $const = new Stmt\ClassConst(array(
+            new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458))
+        ));
+        $method = new Stmt\ClassMethod('doSomething');
+        $contract = $this->builder
+            ->addStmt($method)
+            ->addStmt($const)
+            ->getNode()
+        ;
+
+        $this->assertInstanceOf('PhpParser\Node\Stmt\ClassConst', $contract->stmts[0]);
+        $this->assertInstanceOf('PhpParser\Node\Stmt\ClassMethod', $contract->stmts[1]);
+    }
+
+    public function testDocComment() {
+        $node = $this->builder
+            ->setDocComment('/** Test */')
+            ->getNode();
+
+        $this->assertEquals(new Stmt\Interface_('Contract', array(), array(
+            'comments' => array(new Comment\Doc('/** Test */'))
+        )), $node);
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Unexpected node of type "Stmt_PropertyProperty"
+     */
+    public function testInvalidStmtError() {
+        $this->builder->addStmt(new Stmt\PropertyProperty('invalid'));
+    }
+
+    public function testFullFunctional() {
+        $const = new Stmt\ClassConst(array(
+            new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458))
+        ));
+        $method = new Stmt\ClassMethod('doSomething');
+        $contract = $this->builder
+            ->addStmt($method)
+            ->addStmt($const)
+            ->getNode()
+        ;
+
+        eval($this->dump($contract));
+
+        $this->assertTrue(interface_exists('Contract', false));
+    }
+}
+
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/MethodTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/MethodTest.php
new file mode 100644
index 00000000000..668d13fb323
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/MethodTest.php
@@ -0,0 +1,154 @@
+createMethodBuilder('test')
+            ->makePublic()
+            ->makeAbstract()
+            ->makeStatic()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\ClassMethod('test', array(
+                'type' => Stmt\Class_::MODIFIER_PUBLIC
+                        | Stmt\Class_::MODIFIER_ABSTRACT
+                        | Stmt\Class_::MODIFIER_STATIC,
+                'stmts' => null,
+            )),
+            $node
+        );
+
+        $node = $this->createMethodBuilder('test')
+            ->makeProtected()
+            ->makeFinal()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\ClassMethod('test', array(
+                'type' => Stmt\Class_::MODIFIER_PROTECTED
+                        | Stmt\Class_::MODIFIER_FINAL
+            )),
+            $node
+        );
+
+        $node = $this->createMethodBuilder('test')
+            ->makePrivate()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\ClassMethod('test', array(
+                'type' => Stmt\Class_::MODIFIER_PRIVATE
+            )),
+            $node
+        );
+    }
+
+    public function testReturnByRef() {
+        $node = $this->createMethodBuilder('test')
+            ->makeReturnByRef()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\ClassMethod('test', array(
+                'byRef' => true
+            )),
+            $node
+        );
+    }
+
+    public function testParams() {
+        $param1 = new Node\Param('test1');
+        $param2 = new Node\Param('test2');
+        $param3 = new Node\Param('test3');
+
+        $node = $this->createMethodBuilder('test')
+            ->addParam($param1)
+            ->addParams(array($param2, $param3))
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\ClassMethod('test', array(
+                'params' => array($param1, $param2, $param3)
+            )),
+            $node
+        );
+    }
+
+    public function testStmts() {
+        $stmt1 = new Print_(new String_('test1'));
+        $stmt2 = new Print_(new String_('test2'));
+        $stmt3 = new Print_(new String_('test3'));
+
+        $node = $this->createMethodBuilder('test')
+            ->addStmt($stmt1)
+            ->addStmts(array($stmt2, $stmt3))
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\ClassMethod('test', array(
+                'stmts' => array($stmt1, $stmt2, $stmt3)
+            )),
+            $node
+        );
+    }
+    public function testDocComment() {
+        $node = $this->createMethodBuilder('test')
+            ->setDocComment('/** Test */')
+            ->getNode();
+
+        $this->assertEquals(new Stmt\ClassMethod('test', array(), array(
+            'comments' => array(new Comment\Doc('/** Test */'))
+        )), $node);
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Cannot add statements to an abstract method
+     */
+    public function testAddStmtToAbstractMethodError() {
+        $this->createMethodBuilder('test')
+            ->makeAbstract()
+            ->addStmt(new Print_(new String_('test')))
+        ;
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Cannot make method with statements abstract
+     */
+    public function testMakeMethodWithStmtsAbstractError() {
+        $this->createMethodBuilder('test')
+            ->addStmt(new Print_(new String_('test')))
+            ->makeAbstract()
+        ;
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Expected parameter node, got "Name"
+     */
+    public function testInvalidParamError() {
+        $this->createMethodBuilder('test')
+            ->addParam(new Node\Name('foo'))
+        ;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/NamespaceTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/NamespaceTest.php
new file mode 100644
index 00000000000..54e8c93df6c
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/NamespaceTest.php
@@ -0,0 +1,41 @@
+createNamespaceBuilder('Name\Space')
+            ->addStmt($stmt1)
+            ->addStmts(array($stmt2, $stmt3))
+            ->getNode()
+        ;
+        $this->assertEquals($expected, $node);
+
+        $node = $this->createNamespaceBuilder(new Node\Name(array('Name', 'Space')))
+            ->addStmts(array($stmt1, $stmt2))
+            ->addStmt($stmt3)
+            ->getNode()
+        ;
+        $this->assertEquals($expected, $node);
+
+        $node = $this->createNamespaceBuilder(null)->getNode();
+        $this->assertNull($node->name);
+        $this->assertEmpty($node->stmts);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/ParamTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/ParamTest.php
new file mode 100644
index 00000000000..f71a10c2c0c
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/ParamTest.php
@@ -0,0 +1,124 @@
+createParamBuilder('test')
+            ->setDefault($value)
+            ->getNode()
+        ;
+
+        $this->assertEquals($expectedValueNode, $node->default);
+    }
+
+    public function provideTestDefaultValues() {
+        return array(
+            array(
+                null,
+                new Expr\ConstFetch(new Node\Name('null'))
+            ),
+            array(
+                true,
+                new Expr\ConstFetch(new Node\Name('true'))
+            ),
+            array(
+                false,
+                new Expr\ConstFetch(new Node\Name('false'))
+            ),
+            array(
+                31415,
+                new Scalar\LNumber(31415)
+            ),
+            array(
+                3.1415,
+                new Scalar\DNumber(3.1415)
+            ),
+            array(
+                'Hallo World',
+                new Scalar\String_('Hallo World')
+            ),
+            array(
+                array(1, 2, 3),
+                new Expr\Array_(array(
+                    new Expr\ArrayItem(new Scalar\LNumber(1)),
+                    new Expr\ArrayItem(new Scalar\LNumber(2)),
+                    new Expr\ArrayItem(new Scalar\LNumber(3)),
+                ))
+            ),
+            array(
+                array('foo' => 'bar', 'bar' => 'foo'),
+                new Expr\Array_(array(
+                    new Expr\ArrayItem(
+                        new Scalar\String_('bar'),
+                        new Scalar\String_('foo')
+                    ),
+                    new Expr\ArrayItem(
+                        new Scalar\String_('foo'),
+                        new Scalar\String_('bar')
+                    ),
+                ))
+            ),
+            array(
+                new Scalar\MagicConst\Dir,
+                new Scalar\MagicConst\Dir
+            )
+        );
+    }
+
+    public function testTypeHints() {
+        $node = $this->createParamBuilder('test')
+            ->setTypeHint('array')
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Node\Param('test', null, 'array'),
+            $node
+        );
+
+        $node = $this->createParamBuilder('test')
+            ->setTypeHint('callable')
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Node\Param('test', null, 'callable'),
+            $node
+        );
+
+        $node = $this->createParamBuilder('test')
+            ->setTypeHint('Some\Class')
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Node\Param('test', null, new Node\Name('Some\Class')),
+            $node
+        );
+    }
+
+    public function testByRef() {
+        $node = $this->createParamBuilder('test')
+            ->makeByRef()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Node\Param('test', null, null, true),
+            $node
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/PropertyTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/PropertyTest.php
new file mode 100644
index 00000000000..1e62173dec2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/PropertyTest.php
@@ -0,0 +1,147 @@
+createPropertyBuilder('test')
+            ->makePrivate()
+            ->makeStatic()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Property(
+                Stmt\Class_::MODIFIER_PRIVATE
+              | Stmt\Class_::MODIFIER_STATIC,
+                array(
+                    new Stmt\PropertyProperty('test')
+                )
+            ),
+            $node
+        );
+
+        $node = $this->createPropertyBuilder('test')
+            ->makeProtected()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Property(
+                Stmt\Class_::MODIFIER_PROTECTED,
+                array(
+                    new Stmt\PropertyProperty('test')
+                )
+            ),
+            $node
+        );
+
+        $node = $this->createPropertyBuilder('test')
+            ->makePublic()
+            ->getNode()
+        ;
+
+        $this->assertEquals(
+            new Stmt\Property(
+                Stmt\Class_::MODIFIER_PUBLIC,
+                array(
+                    new Stmt\PropertyProperty('test')
+                )
+            ),
+            $node
+        );
+    }
+
+    public function testDocComment() {
+        $node = $this->createPropertyBuilder('test')
+            ->setDocComment('/** Test */')
+            ->getNode();
+
+        $this->assertEquals(new Stmt\Property(
+            Stmt\Class_::MODIFIER_PUBLIC,
+            array(
+                new Stmt\PropertyProperty('test')
+            ),
+            array(
+                'comments' => array(new Comment\Doc('/** Test */'))
+            )
+        ), $node);
+    }
+
+    /**
+     * @dataProvider provideTestDefaultValues
+     */
+    public function testDefaultValues($value, $expectedValueNode) {
+        $node = $this->createPropertyBuilder('test')
+            ->setDefault($value)
+            ->getNode()
+        ;
+
+        $this->assertEquals($expectedValueNode, $node->props[0]->default);
+    }
+
+    public function provideTestDefaultValues() {
+        return array(
+            array(
+                null,
+                new Expr\ConstFetch(new Name('null'))
+            ),
+            array(
+                true,
+                new Expr\ConstFetch(new Name('true'))
+            ),
+            array(
+                false,
+                new Expr\ConstFetch(new Name('false'))
+            ),
+            array(
+                31415,
+                new Scalar\LNumber(31415)
+            ),
+            array(
+                3.1415,
+                new Scalar\DNumber(3.1415)
+            ),
+            array(
+                'Hallo World',
+                new Scalar\String_('Hallo World')
+            ),
+            array(
+                array(1, 2, 3),
+                new Expr\Array_(array(
+                    new Expr\ArrayItem(new Scalar\LNumber(1)),
+                    new Expr\ArrayItem(new Scalar\LNumber(2)),
+                    new Expr\ArrayItem(new Scalar\LNumber(3)),
+                ))
+            ),
+            array(
+                array('foo' => 'bar', 'bar' => 'foo'),
+                new Expr\Array_(array(
+                    new Expr\ArrayItem(
+                        new Scalar\String_('bar'),
+                        new Scalar\String_('foo')
+                    ),
+                    new Expr\ArrayItem(
+                        new Scalar\String_('foo'),
+                        new Scalar\String_('bar')
+                    ),
+                ))
+            ),
+            array(
+                new Scalar\MagicConst\Dir,
+                new Scalar\MagicConst\Dir
+            )
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/TraitTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/TraitTest.php
new file mode 100644
index 00000000000..a6d69adaf19
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/TraitTest.php
@@ -0,0 +1,47 @@
+createTraitBuilder('TestTrait')
+            ->setDocComment('/** Nice trait */')
+            ->addStmt($method1)
+            ->addStmts(array($method2, $method3))
+            ->addStmt($prop)
+            ->getNode();
+        $this->assertEquals(new Stmt\Trait_('TestTrait', array(
+            $prop, $method1, $method2, $method3
+        ), array(
+            'comments' => array(
+                new Comment\Doc('/** Nice trait */')
+            )
+        )), $trait);
+    }
+
+    /**
+     * @expectedException \LogicException
+     * @expectedExceptionMessage Unexpected node of type "Stmt_Echo"
+     */
+    public function testInvalidStmtError() {
+        $this->createTraitBuilder('Test')
+            ->addStmt(new Stmt\Echo_(array()))
+        ;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/UseTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/UseTest.php
new file mode 100644
index 00000000000..adaeb3a5eda
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Builder/UseTest.php
@@ -0,0 +1,35 @@
+createUseBuilder('Foo\Bar')->getNode();
+        $this->assertEquals(new Stmt\Use_(array(
+            new Stmt\UseUse(new Name('Foo\Bar'), 'Bar')
+        )), $node);
+
+        $node = $this->createUseBuilder(new Name('Foo\Bar'))->as('XYZ')->getNode();
+        $this->assertEquals(new Stmt\Use_(array(
+            new Stmt\UseUse(new Name('Foo\Bar'), 'XYZ')
+        )), $node);
+
+        $node = $this->createUseBuilder('foo\bar', Stmt\Use_::TYPE_FUNCTION)->as('foo')->getNode();
+        $this->assertEquals(new Stmt\Use_(array(
+            new Stmt\UseUse(new Name('foo\bar'), 'foo')
+        ), Stmt\Use_::TYPE_FUNCTION), $node);
+    }
+
+    public function testNonExistingMethod() {
+        $this->setExpectedException('LogicException', 'Method "foo" does not exist');
+        $builder = $this->createUseBuilder('Test');
+        $builder->foo();
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/BuilderFactoryTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/BuilderFactoryTest.php
new file mode 100644
index 00000000000..1c3ef18dfa4
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/BuilderFactoryTest.php
@@ -0,0 +1,108 @@
+assertInstanceOf($className, $factory->$methodName('test'));
+    }
+
+    public function provideTestFactory() {
+        return array(
+            array('namespace', 'PhpParser\Builder\Namespace_'),
+            array('class',     'PhpParser\Builder\Class_'),
+            array('interface', 'PhpParser\Builder\Interface_'),
+            array('trait',     'PhpParser\Builder\Trait_'),
+            array('method',    'PhpParser\Builder\Method'),
+            array('function',  'PhpParser\Builder\Function_'),
+            array('property',  'PhpParser\Builder\Property'),
+            array('param',     'PhpParser\Builder\Param'),
+            array('use',       'PhpParser\Builder\Use_'),
+        );
+    }
+
+    public function testNonExistingMethod() {
+        $this->setExpectedException('LogicException', 'Method "foo" does not exist');
+        $factory = new BuilderFactory();
+        $factory->foo();
+    }
+
+    public function testIntegration() {
+        $factory = new BuilderFactory;
+        $node = $factory->namespace('Name\Space')
+            ->addStmt($factory->use('Foo\Bar\SomeOtherClass'))
+            ->addStmt($factory->use('Foo\Bar')->as('A'))
+            ->addStmt($factory
+                ->class('SomeClass')
+                ->extend('SomeOtherClass')
+                ->implement('A\Few', '\Interfaces')
+                ->makeAbstract()
+
+                ->addStmt($factory->method('firstMethod'))
+
+                ->addStmt($factory->method('someMethod')
+                    ->makePublic()
+                    ->makeAbstract()
+                    ->addParam($factory->param('someParam')->setTypeHint('SomeClass'))
+                    ->setDocComment('/**
+                                      * This method does something.
+                                      *
+                                      * @param SomeClass And takes a parameter
+                                      */'))
+
+                ->addStmt($factory->method('anotherMethod')
+                    ->makeProtected()
+                    ->addParam($factory->param('someParam')->setDefault('test'))
+                    ->addStmt(new Expr\Print_(new Expr\Variable('someParam'))))
+
+                ->addStmt($factory->property('someProperty')->makeProtected())
+                ->addStmt($factory->property('anotherProperty')
+                    ->makePrivate()
+                    ->setDefault(array(1, 2, 3))))
+            ->getNode()
+        ;
+
+        $expected = <<<'EOC'
+prettyPrintFile($stmts);
+
+        $this->assertEquals(
+            str_replace("\r\n", "\n", $expected),
+            str_replace("\r\n", "\n", $generated)
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CodeParsingTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CodeParsingTest.php
new file mode 100644
index 00000000000..0b75342072e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CodeParsingTest.php
@@ -0,0 +1,69 @@
+ array(
+            'startLine', 'endLine', 'startFilePos', 'endFilePos'
+        )));
+        $parser5 = new Parser\Php5($lexer, array(
+            'throwOnError' => false,
+        ));
+        $parser7 = new Parser\Php7($lexer, array(
+            'throwOnError' => false,
+        ));
+
+        $output5 = $this->getParseOutput($parser5, $code);
+        $output7 = $this->getParseOutput($parser7, $code);
+
+        if ($mode === 'php5') {
+            $this->assertSame($expected, $output5, $name);
+            $this->assertNotSame($expected, $output7, $name);
+        } else if ($mode === 'php7') {
+            $this->assertNotSame($expected, $output5, $name);
+            $this->assertSame($expected, $output7, $name);
+        } else {
+            $this->assertSame($expected, $output5, $name);
+            $this->assertSame($expected, $output7, $name);
+        }
+    }
+
+    private function getParseOutput(Parser $parser, $code) {
+        $stmts = $parser->parse($code);
+        $errors = $parser->getErrors();
+
+        $output = '';
+        foreach ($errors as $error) {
+            $output .= $this->formatErrorMessage($error, $code) . "\n";
+        }
+
+        if (null !== $stmts) {
+            $dumper = new NodeDumper;
+            $output .= $dumper->dump($stmts);
+        }
+
+        return canonicalize($output);
+    }
+
+    public function provideTestParse() {
+        return $this->getTests(__DIR__ . '/../code/parser', 'test');
+    }
+
+    private function formatErrorMessage(Error $e, $code) {
+        if ($e->hasColumnInfo()) {
+            return $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code)
+                . ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code);
+        } else {
+            return $e->getMessage();
+        }
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CodeTestAbstract.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CodeTestAbstract.php
new file mode 100644
index 00000000000..16389bb001a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CodeTestAbstract.php
@@ -0,0 +1,59 @@
+getPathname());
+            $fileContents = file_get_contents($fileName);
+
+            // evaluate @@{expr}@@ expressions
+            $fileContents = preg_replace_callback(
+                '/@@\{(.*?)\}@@/',
+                function($matches) {
+                    return eval('return ' . $matches[1] . ';');
+                },
+                $fileContents
+            );
+
+            // parse sections
+            $parts = array_map('trim', explode('-----', $fileContents));
+
+            // first part is the name
+            $name = array_shift($parts) . ' (' . $fileName . ')';
+            $shortName = basename($fileName, '.test');
+
+            // multiple sections possible with always two forming a pair
+            $chunks = array_chunk($parts, 2);
+            foreach ($chunks as $i => $chunk) {
+                $dataSetName = $shortName . (count($chunks) > 1 ? '#' . $i : '');
+                list($expected, $mode) = $this->extractMode(canonicalize($chunk[1]));
+                $tests[$dataSetName] = array($name, $chunk[0], $expected, $mode);
+            }
+        }
+
+        return $tests;
+    }
+
+    private function extractMode($expected) {
+        $firstNewLine = strpos($expected, "\n");
+        if (false === $firstNewLine) {
+            $firstNewLine = strlen($expected);
+        }
+
+        $firstLine = substr($expected, 0, $firstNewLine);
+        if (0 !== strpos($firstLine, '!!')) {
+            return [$expected, null];
+        }
+
+        $expected = (string) substr($expected, $firstNewLine + 1);
+        return [$expected, substr($firstLine, 2)];
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CommentTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CommentTest.php
new file mode 100644
index 00000000000..96db6e684f7
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/CommentTest.php
@@ -0,0 +1,71 @@
+assertSame('/* Some comment */', $comment->getText());
+        $this->assertSame('/* Some comment */', (string) $comment);
+        $this->assertSame(1, $comment->getLine());
+
+        $comment->setText('/* Some other comment */');
+        $comment->setLine(10);
+
+        $this->assertSame('/* Some other comment */', $comment->getText());
+        $this->assertSame('/* Some other comment */', (string) $comment);
+        $this->assertSame(10, $comment->getLine());
+    }
+
+    /**
+     * @dataProvider provideTestReformatting
+     */
+    public function testReformatting($commentText, $reformattedText) {
+        $comment = new Comment($commentText);
+        $this->assertSame($reformattedText, $comment->getReformattedText());
+    }
+
+    public function provideTestReformatting() {
+        return array(
+            array('// Some text' . "\n", '// Some text'),
+            array('/* Some text */', '/* Some text */'),
+            array(
+                '/**
+     * Some text.
+     * Some more text.
+     */',
+                '/**
+ * Some text.
+ * Some more text.
+ */'
+            ),
+            array(
+                '/*
+        Some text.
+        Some more text.
+    */',
+                '/*
+    Some text.
+    Some more text.
+*/'
+            ),
+            array(
+                '/* Some text.
+       More text.
+       Even more text. */',
+                '/* Some text.
+   More text.
+   Even more text. */'
+            ),
+            // invalid comment -> no reformatting
+            array(
+                'hallo
+    world',
+                'hallo
+    world',
+            ),
+        );
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/ErrorTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/ErrorTest.php
new file mode 100644
index 00000000000..021a7f89113
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/ErrorTest.php
@@ -0,0 +1,112 @@
+ 10,
+            'endLine' => 11,
+        );
+        $error = new Error('Some error', $attributes);
+
+        $this->assertSame('Some error', $error->getRawMessage());
+        $this->assertSame($attributes, $error->getAttributes());
+        $this->assertSame(10, $error->getStartLine());
+        $this->assertSame(11, $error->getEndLine());
+        $this->assertSame(10, $error->getRawLine());
+        $this->assertSame('Some error on line 10', $error->getMessage());
+
+        return $error;
+    }
+
+    /**
+     * @depends testConstruct
+     */
+    public function testSetMessageAndLine(Error $error) {
+        $error->setRawMessage('Some other error');
+        $this->assertSame('Some other error', $error->getRawMessage());
+
+        $error->setStartLine(15);
+        $this->assertSame(15, $error->getStartLine());
+        $this->assertSame('Some other error on line 15', $error->getMessage());
+
+        $error->setRawLine(17);
+        $this->assertSame(17, $error->getRawLine());
+        $this->assertSame('Some other error on line 17', $error->getMessage());
+    }
+
+    public function testUnknownLine() {
+        $error = new Error('Some error');
+
+        $this->assertSame(-1, $error->getStartLine());
+        $this->assertSame(-1, $error->getEndLine());
+        $this->assertSame(-1, $error->getRawLine());
+        $this->assertSame('Some error on unknown line', $error->getMessage());
+    }
+
+    /** @dataProvider provideTestColumnInfo */
+    public function testColumnInfo($code, $startPos, $endPos, $startColumn, $endColumn) {
+        $error = new Error('Some error', array(
+            'startFilePos' => $startPos,
+            'endFilePos' => $endPos,
+        ));
+
+        $this->assertSame(true, $error->hasColumnInfo());
+        $this->assertSame($startColumn, $error->getStartColumn($code));
+        $this->assertSame($endColumn, $error->getEndColumn($code));
+
+    }
+
+    public function provideTestColumnInfo() {
+        return array(
+            // Error at "bar"
+            array("assertSame(false, $error->hasColumnInfo());
+        try {
+            $error->getStartColumn('');
+            $this->fail('Expected RuntimeException');
+        } catch (\RuntimeException $e) {
+            $this->assertSame('Error does not have column information', $e->getMessage());
+        }
+        try {
+            $error->getEndColumn('');
+            $this->fail('Expected RuntimeException');
+        } catch (\RuntimeException $e) {
+            $this->assertSame('Error does not have column information', $e->getMessage());
+        }
+    }
+
+    /**
+     * @expectedException \RuntimeException
+     * @expectedExceptionMessage Invalid position information
+     */
+    public function testInvalidPosInfo() {
+        $error = new Error('Some error', array(
+            'startFilePos' => 10,
+            'endFilePos' => 11,
+        ));
+        $error->getStartColumn('code');
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Lexer/EmulativeTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Lexer/EmulativeTest.php
new file mode 100644
index 00000000000..f1fe6183aa0
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Lexer/EmulativeTest.php
@@ -0,0 +1,133 @@
+getLexer();
+        $lexer->startLexing('assertSame($expectedToken, $lexer->getNextToken());
+        $this->assertSame(0, $lexer->getNextToken());
+    }
+
+    /**
+     * @dataProvider provideTestReplaceKeywords
+     */
+    public function testNoReplaceKeywordsAfterObjectOperator($keyword) {
+        $lexer = $this->getLexer();
+        $lexer->startLexing('' . $keyword);
+
+        $this->assertSame(Tokens::T_OBJECT_OPERATOR, $lexer->getNextToken());
+        $this->assertSame(Tokens::T_STRING, $lexer->getNextToken());
+        $this->assertSame(0, $lexer->getNextToken());
+    }
+
+    public function provideTestReplaceKeywords() {
+        return array(
+            // PHP 5.5
+            array('finally',       Tokens::T_FINALLY),
+            array('yield',         Tokens::T_YIELD),
+
+            // PHP 5.4
+            array('callable',      Tokens::T_CALLABLE),
+            array('insteadof',     Tokens::T_INSTEADOF),
+            array('trait',         Tokens::T_TRAIT),
+            array('__TRAIT__',     Tokens::T_TRAIT_C),
+
+            // PHP 5.3
+            array('__DIR__',       Tokens::T_DIR),
+            array('goto',          Tokens::T_GOTO),
+            array('namespace',     Tokens::T_NAMESPACE),
+            array('__NAMESPACE__', Tokens::T_NS_C),
+        );
+    }
+
+    /**
+     * @dataProvider provideTestLexNewFeatures
+     */
+    public function testLexNewFeatures($code, array $expectedTokens) {
+        $lexer = $this->getLexer();
+        $lexer->startLexing('assertSame($expectedTokenType, $lexer->getNextToken($text));
+            $this->assertSame($expectedTokenText, $text);
+        }
+        $this->assertSame(0, $lexer->getNextToken());
+    }
+
+    /**
+     * @dataProvider provideTestLexNewFeatures
+     */
+    public function testLeaveStuffAloneInStrings($code) {
+        $stringifiedToken = '"' . addcslashes($code, '"\\') . '"';
+
+        $lexer = $this->getLexer();
+        $lexer->startLexing('assertSame(Tokens::T_CONSTANT_ENCAPSED_STRING, $lexer->getNextToken($text));
+        $this->assertSame($stringifiedToken, $text);
+        $this->assertSame(0, $lexer->getNextToken());
+    }
+
+    public function provideTestLexNewFeatures() {
+        return array(
+            array('yield from', array(
+                array(Tokens::T_YIELD_FROM, 'yield from'),
+            )),
+            array("yield\r\nfrom", array(
+                array(Tokens::T_YIELD_FROM, "yield\r\nfrom"),
+            )),
+            array('...', array(
+                array(Tokens::T_ELLIPSIS, '...'),
+            )),
+            array('**', array(
+                array(Tokens::T_POW, '**'),
+            )),
+            array('**=', array(
+                array(Tokens::T_POW_EQUAL, '**='),
+            )),
+            array('??', array(
+                array(Tokens::T_COALESCE, '??'),
+            )),
+            array('<=>', array(
+                array(Tokens::T_SPACESHIP, '<=>'),
+            )),
+            array('0b1010110', array(
+                array(Tokens::T_LNUMBER, '0b1010110'),
+            )),
+            array('0b1011010101001010110101010010101011010101010101101011001110111100', array(
+                array(Tokens::T_DNUMBER, '0b1011010101001010110101010010101011010101010101101011001110111100'),
+            )),
+            array('\\', array(
+                array(Tokens::T_NS_SEPARATOR, '\\'),
+            )),
+            array("<<<'NOWDOC'\nNOWDOC;\n", array(
+                array(Tokens::T_START_HEREDOC, "<<<'NOWDOC'\n"),
+                array(Tokens::T_END_HEREDOC, 'NOWDOC'),
+                array(ord(';'), ';'),
+            )),
+            array("<<<'NOWDOC'\nFoobar\nNOWDOC;\n", array(
+                array(Tokens::T_START_HEREDOC, "<<<'NOWDOC'\n"),
+                array(Tokens::T_ENCAPSED_AND_WHITESPACE, "Foobar\n"),
+                array(Tokens::T_END_HEREDOC, 'NOWDOC'),
+                array(ord(';'), ';'),
+            )),
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/LexerTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/LexerTest.php
new file mode 100644
index 00000000000..6adad6c2e9f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/LexerTest.php
@@ -0,0 +1,248 @@
+markTestSkipped('HHVM does not throw warnings from token_get_all()');
+        }
+
+        $lexer = $this->getLexer();
+        try {
+            $lexer->startLexing($code);
+        } catch (Error $e) {
+            $this->assertSame($message, $e->getMessage());
+
+            return;
+        }
+
+        $this->fail('Expected PhpParser\Error');
+    }
+
+    public function provideTestError() {
+        return array(
+            array('getLexer($options);
+        $lexer->startLexing($code);
+        while ($id = $lexer->getNextToken($value, $startAttributes, $endAttributes)) {
+            $token = array_shift($tokens);
+
+            $this->assertSame($token[0], $id);
+            $this->assertSame($token[1], $value);
+            $this->assertEquals($token[2], $startAttributes);
+            $this->assertEquals($token[3], $endAttributes);
+        }
+    }
+
+    public function provideTestLex() {
+        return array(
+            // tests conversion of closing PHP tag and drop of whitespace and opening tags
+            array(
+                'plaintext',
+                array(),
+                array(
+                    array(
+                        Tokens::T_STRING, 'tokens',
+                        array('startLine' => 1), array('endLine' => 1)
+                    ),
+                    array(
+                        ord(';'), '?>',
+                        array('startLine' => 1), array('endLine' => 1)
+                    ),
+                    array(
+                        Tokens::T_INLINE_HTML, 'plaintext',
+                        array('startLine' => 1), array('endLine' => 1)
+                    ),
+                )
+            ),
+            // tests line numbers
+            array(
+                ' 2), array('endLine' => 2)
+                    ),
+                    array(
+                        Tokens::T_STRING, 'token',
+                        array('startLine' => 2), array('endLine' => 2)
+                    ),
+                    array(
+                        ord('$'), '$',
+                        array(
+                            'startLine' => 3,
+                            'comments' => array(new Comment\Doc('/** doc' . "\n" . 'comment */', 2))
+                        ),
+                        array('endLine' => 3)
+                    ),
+                )
+            ),
+            // tests comment extraction
+            array(
+                ' 2,
+                            'comments' => array(
+                                new Comment('/* comment */', 1),
+                                new Comment('// comment' . "\n", 1),
+                                new Comment\Doc('/** docComment 1 */', 2),
+                                new Comment\Doc('/** docComment 2 */', 2),
+                            ),
+                        ),
+                        array('endLine' => 2)
+                    ),
+                )
+            ),
+            // tests differing start and end line
+            array(
+                ' 1), array('endLine' => 2)
+                    ),
+                )
+            ),
+            // tests exact file offsets
+            array(
+                ' array('startFilePos', 'endFilePos')),
+                array(
+                    array(
+                        Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"',
+                        array('startFilePos' => 6), array('endFilePos' => 8)
+                    ),
+                    array(
+                        ord(';'), ';',
+                        array('startFilePos' => 9), array('endFilePos' => 9)
+                    ),
+                    array(
+                        Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"',
+                        array('startFilePos' => 18), array('endFilePos' => 20)
+                    ),
+                    array(
+                        ord(';'), ';',
+                        array('startFilePos' => 21), array('endFilePos' => 21)
+                    ),
+                )
+            ),
+            // tests token offsets
+            array(
+                ' array('startTokenPos', 'endTokenPos')),
+                array(
+                    array(
+                        Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"',
+                        array('startTokenPos' => 1), array('endTokenPos' => 1)
+                    ),
+                    array(
+                        ord(';'), ';',
+                        array('startTokenPos' => 2), array('endTokenPos' => 2)
+                    ),
+                    array(
+                        Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"',
+                        array('startTokenPos' => 5), array('endTokenPos' => 5)
+                    ),
+                    array(
+                        ord(';'), ';',
+                        array('startTokenPos' => 6), array('endTokenPos' => 6)
+                    ),
+                )
+            ),
+            // tests all attributes being disabled
+            array(
+                ' array()),
+                array(
+                    array(
+                        Tokens::T_VARIABLE, '$bar',
+                        array(), array()
+                    ),
+                    array(
+                        ord(';'), ';',
+                        array(), array()
+                    )
+                )
+            )
+        );
+    }
+
+    /**
+     * @dataProvider provideTestHaltCompiler
+     */
+    public function testHandleHaltCompiler($code, $remaining) {
+        $lexer = $this->getLexer();
+        $lexer->startLexing($code);
+
+        while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken());
+
+        $this->assertSame($remaining, $lexer->handleHaltCompiler());
+        $this->assertSame(0, $lexer->getNextToken());
+    }
+
+    public function provideTestHaltCompiler() {
+        return array(
+            array('Remaining Text', 'Remaining Text'),
+            //array('getLexer();
+        $lexer->startLexing('getNextToken());
+        $lexer->handleHaltCompiler();
+    }
+
+    public function testGetTokens() {
+        $code = 'getLexer();
+        $lexer->startLexing($code);
+        $this->assertSame($expectedTokens, $lexer->getTokens());
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/NameTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/NameTest.php
new file mode 100644
index 00000000000..33785511fe5
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/NameTest.php
@@ -0,0 +1,164 @@
+assertSame(array('foo', 'bar'), $name->parts);
+
+        $name = new Name('foo\bar');
+        $this->assertSame(array('foo', 'bar'), $name->parts);
+    }
+
+    public function testGet() {
+        $name = new Name('foo');
+        $this->assertSame('foo', $name->getFirst());
+        $this->assertSame('foo', $name->getLast());
+
+        $name = new Name('foo\bar');
+        $this->assertSame('foo', $name->getFirst());
+        $this->assertSame('bar', $name->getLast());
+    }
+
+    public function testToString() {
+        $name = new Name('foo\bar');
+
+        $this->assertSame('foo\bar', (string) $name);
+        $this->assertSame('foo\bar', $name->toString());
+        $this->assertSame('foo_bar', $name->toString('_'));
+    }
+
+    public function testSet() {
+        $name = new Name('foo');
+
+        $name->set('foo\bar');
+        $this->assertSame('foo\bar', $name->toString());
+
+        $name->set(array('foo', 'bar'));
+        $this->assertSame('foo\bar', $name->toString());
+
+        $name->set(new Name('foo\bar'));
+        $this->assertSame('foo\bar', $name->toString());
+    }
+
+    public function testSetFirst() {
+        $name = new Name('foo');
+
+        $name->setFirst('bar');
+        $this->assertSame('bar', $name->toString());
+
+        $name->setFirst('A\B');
+        $this->assertSame('A\B', $name->toString());
+
+        $name->setFirst('C');
+        $this->assertSame('C\B', $name->toString());
+
+        $name->setFirst('D\E');
+        $this->assertSame('D\E\B', $name->toString());
+    }
+
+    public function testSetLast() {
+        $name = new Name('foo');
+
+        $name->setLast('bar');
+        $this->assertSame('bar', $name->toString());
+
+        $name->setLast('A\B');
+        $this->assertSame('A\B', $name->toString());
+
+        $name->setLast('C');
+        $this->assertSame('A\C', $name->toString());
+
+        $name->setLast('D\E');
+        $this->assertSame('A\D\E', $name->toString());
+    }
+
+    public function testAppend() {
+        $name = new Name('foo');
+
+        $name->append('bar');
+        $this->assertSame('foo\bar', $name->toString());
+
+        $name->append('bar\foo');
+        $this->assertSame('foo\bar\bar\foo', $name->toString());
+    }
+
+    public function testPrepend() {
+        $name = new Name('foo');
+
+        $name->prepend('bar');
+        $this->assertSame('bar\foo', $name->toString());
+
+        $name->prepend('foo\bar');
+        $this->assertSame('foo\bar\bar\foo', $name->toString());
+    }
+
+    public function testSlice() {
+        $name = new Name('foo\bar');
+        $this->assertEquals(new Name('foo\bar'), $name->slice(0));
+        $this->assertEquals(new Name('bar'), $name->slice(1));
+        $this->assertEquals(new Name([]), $name->slice(2));
+    }
+
+    /**
+     * @expectedException \OutOfBoundsException
+     * @expectedExceptionMessage Offset 4 is out of bounds
+     */
+    public function testSliceException() {
+        (new Name('foo\bar\baz'))->slice(4);
+    }
+
+    public function testConcat() {
+        $this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz'));
+        $this->assertEquals(
+            new Name\FullyQualified('foo\bar'),
+            Name\FullyQualified::concat(['foo'], new Name('bar'))
+        );
+
+        $attributes = ['foo' => 'bar'];
+        $this->assertEquals(
+            new Name\Relative('foo\bar\baz', $attributes),
+            Name\Relative::concat(new Name\FullyQualified('foo\bar'), 'baz', $attributes)
+        );
+
+        $this->assertEquals(new Name('foo'), Name::concat([], 'foo'));
+        $this->assertEquals(new Name([]), Name::concat([], []));
+    }
+
+    public function testIs() {
+        $name = new Name('foo');
+        $this->assertTrue ($name->isUnqualified());
+        $this->assertFalse($name->isQualified());
+        $this->assertFalse($name->isFullyQualified());
+        $this->assertFalse($name->isRelative());
+
+        $name = new Name('foo\bar');
+        $this->assertFalse($name->isUnqualified());
+        $this->assertTrue ($name->isQualified());
+        $this->assertFalse($name->isFullyQualified());
+        $this->assertFalse($name->isRelative());
+
+        $name = new Name\FullyQualified('foo');
+        $this->assertFalse($name->isUnqualified());
+        $this->assertFalse($name->isQualified());
+        $this->assertTrue ($name->isFullyQualified());
+        $this->assertFalse($name->isRelative());
+
+        $name = new Name\Relative('foo');
+        $this->assertFalse($name->isUnqualified());
+        $this->assertFalse($name->isQualified());
+        $this->assertFalse($name->isFullyQualified());
+        $this->assertTrue ($name->isRelative());
+    }
+
+    /**
+     * @expectedException        \InvalidArgumentException
+     * @expectedExceptionMessage When changing a name you need to pass either a string, an array or a Name node
+     */
+    public function testInvalidArg() {
+        $name = new Name('foo');
+        $name->set(new \stdClass);
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Scalar/MagicConstTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Scalar/MagicConstTest.php
new file mode 100644
index 00000000000..3141f563d18
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Scalar/MagicConstTest.php
@@ -0,0 +1,25 @@
+assertSame($name, $magicConst->getName());
+    }
+
+    public function provideTestGetName() {
+        return array(
+            array(new MagicConst\Class_, '__CLASS__'),
+            array(new MagicConst\Dir, '__DIR__'),
+            array(new MagicConst\File, '__FILE__'),
+            array(new MagicConst\Function_, '__FUNCTION__'),
+            array(new MagicConst\Line, '__LINE__'),
+            array(new MagicConst\Method, '__METHOD__'),
+            array(new MagicConst\Namespace_, '__NAMESPACE__'),
+            array(new MagicConst\Trait_, '__TRAIT__'),
+        );
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Scalar/StringTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Scalar/StringTest.php
new file mode 100644
index 00000000000..be390357821
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Scalar/StringTest.php
@@ -0,0 +1,61 @@
+assertSame(
+            $expected,
+            String_::parseEscapeSequences($string, $quote)
+        );
+    }
+
+    /**
+     * @dataProvider provideTestParse
+     */
+    public function testCreate($expected, $string) {
+        $this->assertSame(
+            $expected,
+            String_::parse($string)
+        );
+    }
+
+    public function provideTestParseEscapeSequences() {
+        return array(
+            array('"',              '\\"',              '"'),
+            array('\\"',            '\\"',              '`'),
+            array('\\"\\`',         '\\"\\`',           null),
+            array("\\\$\n\r\t\f\v", '\\\\\$\n\r\t\f\v', null),
+            array("\x1B",           '\e',               null),
+            array(chr(255),         '\xFF',             null),
+            array(chr(255),         '\377',             null),
+            array(chr(0),           '\400',             null),
+            array("\0",             '\0',               null),
+            array('\xFF',           '\\\\xFF',          null),
+        );
+    }
+
+    public function provideTestParse() {
+        $tests = array(
+            array('A', '\'A\''),
+            array('A', 'b\'A\''),
+            array('A', '"A"'),
+            array('A', 'b"A"'),
+            array('\\', '\'\\\\\''),
+            array('\'', '\'\\\'\''),
+        );
+
+        foreach ($this->provideTestParseEscapeSequences() as $i => $test) {
+            // skip second and third tests, they aren't for double quotes
+            if ($i != 1 && $i != 2) {
+                $tests[] = array($test[0], '"' . $test[1] . '"');
+            }
+        }
+
+        return $tests;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/ClassMethodTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/ClassMethodTest.php
new file mode 100644
index 00000000000..fa8aed8088e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/ClassMethodTest.php
@@ -0,0 +1,63 @@
+ constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier))
+        ));
+
+        $this->assertTrue($node->{'is' . $modifier}());
+    }
+
+    public function testNoModifiers() {
+        $node = new ClassMethod('foo', array('type' => 0));
+
+        $this->assertTrue($node->isPublic());
+        $this->assertFalse($node->isProtected());
+        $this->assertFalse($node->isPrivate());
+        $this->assertFalse($node->isAbstract());
+        $this->assertFalse($node->isFinal());
+        $this->assertFalse($node->isStatic());
+    }
+
+    public function provideModifiers() {
+        return array(
+            array('public'),
+            array('protected'),
+            array('private'),
+            array('abstract'),
+            array('final'),
+            array('static'),
+        );
+    }
+
+    /**
+     * Checks that implicit public modifier detection for method is working
+     *
+     * @dataProvider implicitPublicModifiers
+     *
+     * @param integer $modifier Node type modifier
+     */
+    public function testImplicitPublic($modifier)
+    {
+        $node = new ClassMethod('foo', array(
+            'type' => constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier))
+        ));
+
+        $this->assertTrue($node->isPublic(), 'Node should be implicitly public');
+    }
+
+    public function implicitPublicModifiers() {
+        return array(
+            array('abstract'),
+            array('final'),
+            array('static'),
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/ClassTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/ClassTest.php
new file mode 100644
index 00000000000..643b15cb1cb
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/ClassTest.php
@@ -0,0 +1,59 @@
+ Class_::MODIFIER_ABSTRACT));
+        $this->assertTrue($class->isAbstract());
+
+        $class = new Class_('Foo');
+        $this->assertFalse($class->isAbstract());
+    }
+
+    public function testIsFinal() {
+        $class = new Class_('Foo', array('type' => Class_::MODIFIER_FINAL));
+        $this->assertTrue($class->isFinal());
+
+        $class = new Class_('Foo');
+        $this->assertFalse($class->isFinal());
+    }
+
+    public function testGetMethods() {
+        $methods = array(
+            new ClassMethod('foo'),
+            new ClassMethod('bar'),
+            new ClassMethod('fooBar'),
+        );
+        $class = new Class_('Foo', array(
+            'stmts' => array(
+                new TraitUse(array()),
+                $methods[0],
+                new ClassConst(array()),
+                $methods[1],
+                new Property(0, array()),
+                $methods[2],
+            )
+        ));
+
+        $this->assertSame($methods, $class->getMethods());
+    }
+
+    public function testGetMethod() {
+        $methodConstruct = new ClassMethod('__CONSTRUCT');
+        $methodTest = new ClassMethod('test');
+        $class = new Class_('Foo', array(
+            'stmts' => array(
+                new ClassConst(array()),
+                $methodConstruct,
+                new Property(0, array()),
+                $methodTest,
+            )
+        ));
+
+        $this->assertSame($methodConstruct, $class->getMethod('__construct'));
+        $this->assertSame($methodTest, $class->getMethod('test'));
+        $this->assertNull($class->getMethod('nonExisting'));
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/InterfaceTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/InterfaceTest.php
new file mode 100644
index 00000000000..c499058265e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/InterfaceTest.php
@@ -0,0 +1,26 @@
+ array(
+                new Node\Stmt\ClassConst(array(new Node\Const_('C1', new Node\Scalar\String_('C1')))),
+                $methods[0],
+                new Node\Stmt\ClassConst(array(new Node\Const_('C2', new Node\Scalar\String_('C2')))),
+                $methods[1],
+                new Node\Stmt\ClassConst(array(new Node\Const_('C3', new Node\Scalar\String_('C3')))),
+            )
+        ));
+
+        $this->assertSame($methods, $interface->getMethods());
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/PropertyTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/PropertyTest.php
new file mode 100644
index 00000000000..bcfc0c6bd70
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Node/Stmt/PropertyTest.php
@@ -0,0 +1,44 @@
+assertTrue($node->{'is' . $modifier}());
+    }
+
+    public function testNoModifiers() {
+        $node = new Property(0, array());
+
+        $this->assertTrue($node->isPublic());
+        $this->assertFalse($node->isProtected());
+        $this->assertFalse($node->isPrivate());
+        $this->assertFalse($node->isStatic());
+    }
+
+    public function testStaticImplicitlyPublic() {
+        $node = new Property(Class_::MODIFIER_STATIC, array());
+        $this->assertTrue($node->isPublic());
+        $this->assertFalse($node->isProtected());
+        $this->assertFalse($node->isPrivate());
+        $this->assertTrue($node->isStatic());
+    }
+
+    public function provideModifiers() {
+        return array(
+            array('public'),
+            array('protected'),
+            array('private'),
+            array('static'),
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeAbstractTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeAbstractTest.php
new file mode 100644
index 00000000000..40e0bb8833a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeAbstractTest.php
@@ -0,0 +1,147 @@
+subNode1 = $subNode1;
+        $this->subNode2 = $subNode2;
+    }
+
+    public function getSubNodeNames() {
+        return array('subNode1', 'subNode2');
+    }
+
+    // This method is only overwritten because the node is located in an unusual namespace
+    public function getType() {
+        return 'Dummy';
+    }
+}
+
+class NodeAbstractTest extends \PHPUnit_Framework_TestCase
+{
+    public function provideNodes() {
+        $attributes = array(
+            'startLine' => 10,
+            'comments'  => array(
+                new Comment('// Comment' . "\n"),
+                new Comment\Doc('/** doc comment */'),
+            ),
+        );
+
+        $node = new DummyNode('value1', 'value2', $attributes);
+        $node->notSubNode = 'value3';
+
+        return array(
+            array($attributes, $node),
+        );
+    }
+
+    /**
+     * @dataProvider provideNodes
+     */
+    public function testConstruct(array $attributes, Node $node) {
+        $this->assertSame('Dummy', $node->getType());
+        $this->assertSame(array('subNode1', 'subNode2'), $node->getSubNodeNames());
+        $this->assertSame(10, $node->getLine());
+        $this->assertSame('/** doc comment */', $node->getDocComment()->getText());
+        $this->assertSame('value1', $node->subNode1);
+        $this->assertSame('value2', $node->subNode2);
+        $this->assertTrue(isset($node->subNode1));
+        $this->assertTrue(isset($node->subNode2));
+        $this->assertFalse(isset($node->subNode3));
+        $this->assertSame($attributes, $node->getAttributes());
+
+        return $node;
+    }
+
+    /**
+     * @dataProvider provideNodes
+     */
+    public function testGetDocComment(array $attributes, Node $node) {
+        $this->assertSame('/** doc comment */', $node->getDocComment()->getText());
+        array_pop($node->getAttribute('comments')); // remove doc comment
+        $this->assertNull($node->getDocComment());
+        array_pop($node->getAttribute('comments')); // remove comment
+        $this->assertNull($node->getDocComment());
+    }
+
+    /**
+     * @dataProvider provideNodes
+     */
+    public function testChange(array $attributes, Node $node) {
+        // change of line
+        $node->setLine(15);
+        $this->assertSame(15, $node->getLine());
+
+        // direct modification
+        $node->subNode = 'newValue';
+        $this->assertSame('newValue', $node->subNode);
+
+        // indirect modification
+        $subNode =& $node->subNode;
+        $subNode = 'newNewValue';
+        $this->assertSame('newNewValue', $node->subNode);
+
+        // removal
+        unset($node->subNode);
+        $this->assertFalse(isset($node->subNode));
+    }
+
+    /**
+     * @dataProvider provideNodes
+     */
+    public function testIteration(array $attributes, Node $node) {
+        // Iteration is simple object iteration over properties,
+        // not over subnodes
+        $i = 0;
+        foreach ($node as $key => $value) {
+            if ($i === 0) {
+                $this->assertSame('subNode1', $key);
+                $this->assertSame('value1', $value);
+            } else if ($i === 1) {
+                $this->assertSame('subNode2', $key);
+                $this->assertSame('value2', $value);
+            } else if ($i === 2) {
+                $this->assertSame('notSubNode', $key);
+                $this->assertSame('value3', $value);
+            } else {
+                throw new \Exception;
+            }
+            $i++;
+        }
+        $this->assertSame(3, $i);
+    }
+
+    public function testAttributes() {
+        /** @var $node Node */
+        $node = $this->getMockForAbstractClass('PhpParser\NodeAbstract');
+
+        $this->assertEmpty($node->getAttributes());
+
+        $node->setAttribute('key', 'value');
+        $this->assertTrue($node->hasAttribute('key'));
+        $this->assertSame('value', $node->getAttribute('key'));
+
+        $this->assertFalse($node->hasAttribute('doesNotExist'));
+        $this->assertNull($node->getAttribute('doesNotExist'));
+        $this->assertSame('default', $node->getAttribute('doesNotExist', 'default'));
+
+        $node->setAttribute('null', null);
+        $this->assertTrue($node->hasAttribute('null'));
+        $this->assertNull($node->getAttribute('null'));
+        $this->assertNull($node->getAttribute('null', 'default'));
+
+        $this->assertSame(
+            array(
+                'key'  => 'value',
+                'null' => null,
+            ),
+            $node->getAttributes()
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeDumperTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeDumperTest.php
new file mode 100644
index 00000000000..306bc20d54e
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeDumperTest.php
@@ -0,0 +1,72 @@
+assertSame($this->canonicalize($dump), $this->canonicalize($dumper->dump($node)));
+    }
+
+    public function provideTestDump() {
+        return array(
+            array(
+                array(),
+'array(
+)'
+            ),
+            array(
+                array('Foo', 'Bar', 'Key' => 'FooBar'),
+'array(
+    0: Foo
+    1: Bar
+    Key: FooBar
+)'
+            ),
+            array(
+                new Node\Name(array('Hallo', 'World')),
+'Name(
+    parts: array(
+        0: Hallo
+        1: World
+    )
+)'
+            ),
+            array(
+                new Node\Expr\Array_(array(
+                    new Node\Expr\ArrayItem(new Node\Scalar\String_('Foo'))
+                )),
+'Expr_Array(
+    items: array(
+        0: Expr_ArrayItem(
+            key: null
+            value: Scalar_String(
+                value: Foo
+            )
+            byRef: false
+        )
+    )
+)'
+            ),
+        );
+    }
+
+    /**
+     * @expectedException        \InvalidArgumentException
+     * @expectedExceptionMessage Can only dump nodes and arrays.
+     */
+    public function testError() {
+        $dumper = new NodeDumper;
+        $dumper->dump(new \stdClass);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeTraverserTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeTraverserTest.php
new file mode 100644
index 00000000000..a46cf573e11
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeTraverserTest.php
@@ -0,0 +1,203 @@
+getMock('PhpParser\NodeVisitor');
+
+        $visitor->expects($this->at(0))->method('beforeTraverse')->with($stmts);
+        $visitor->expects($this->at(1))->method('enterNode')->with($echoNode);
+        $visitor->expects($this->at(2))->method('enterNode')->with($str1Node);
+        $visitor->expects($this->at(3))->method('leaveNode')->with($str1Node);
+        $visitor->expects($this->at(4))->method('enterNode')->with($str2Node);
+        $visitor->expects($this->at(5))->method('leaveNode')->with($str2Node);
+        $visitor->expects($this->at(6))->method('leaveNode')->with($echoNode);
+        $visitor->expects($this->at(7))->method('afterTraverse')->with($stmts);
+
+        $traverser = new NodeTraverser;
+        $traverser->addVisitor($visitor);
+
+        $this->assertEquals($stmts, $traverser->traverse($stmts));
+    }
+
+    public function testModifying() {
+        $str1Node  = new String_('Foo');
+        $str2Node  = new String_('Bar');
+        $printNode = new Expr\Print_($str1Node);
+
+        // first visitor changes the node, second verifies the change
+        $visitor1 = $this->getMock('PhpParser\NodeVisitor');
+        $visitor2 = $this->getMock('PhpParser\NodeVisitor');
+
+        // replace empty statements with string1 node
+        $visitor1->expects($this->at(0))->method('beforeTraverse')->with(array())
+                 ->will($this->returnValue(array($str1Node)));
+        $visitor2->expects($this->at(0))->method('beforeTraverse')->with(array($str1Node));
+
+        // replace string1 node with print node
+        $visitor1->expects($this->at(1))->method('enterNode')->with($str1Node)
+                 ->will($this->returnValue($printNode));
+        $visitor2->expects($this->at(1))->method('enterNode')->with($printNode);
+
+        // replace string1 node with string2 node
+        $visitor1->expects($this->at(2))->method('enterNode')->with($str1Node)
+                 ->will($this->returnValue($str2Node));
+        $visitor2->expects($this->at(2))->method('enterNode')->with($str2Node);
+
+        // replace string2 node with string1 node again
+        $visitor1->expects($this->at(3))->method('leaveNode')->with($str2Node)
+                 ->will($this->returnValue($str1Node));
+        $visitor2->expects($this->at(3))->method('leaveNode')->with($str1Node);
+
+        // replace print node with string1 node again
+        $visitor1->expects($this->at(4))->method('leaveNode')->with($printNode)
+                 ->will($this->returnValue($str1Node));
+        $visitor2->expects($this->at(4))->method('leaveNode')->with($str1Node);
+
+        // replace string1 node with empty statements again
+        $visitor1->expects($this->at(5))->method('afterTraverse')->with(array($str1Node))
+                 ->will($this->returnValue(array()));
+        $visitor2->expects($this->at(5))->method('afterTraverse')->with(array());
+
+        $traverser = new NodeTraverser;
+        $traverser->addVisitor($visitor1);
+        $traverser->addVisitor($visitor2);
+
+        // as all operations are reversed we end where we start
+        $this->assertEquals(array(), $traverser->traverse(array()));
+    }
+
+    public function testRemove() {
+        $str1Node = new String_('Foo');
+        $str2Node = new String_('Bar');
+
+        $visitor = $this->getMock('PhpParser\NodeVisitor');
+
+        // remove the string1 node, leave the string2 node
+        $visitor->expects($this->at(2))->method('leaveNode')->with($str1Node)
+                ->will($this->returnValue(false));
+
+        $traverser = new NodeTraverser;
+        $traverser->addVisitor($visitor);
+
+        $this->assertEquals(array($str2Node), $traverser->traverse(array($str1Node, $str2Node)));
+    }
+
+    public function testMerge() {
+        $strStart  = new String_('Start');
+        $strMiddle = new String_('End');
+        $strEnd    = new String_('Middle');
+        $strR1     = new String_('Replacement 1');
+        $strR2     = new String_('Replacement 2');
+
+        $visitor = $this->getMock('PhpParser\NodeVisitor');
+
+        // replace strMiddle with strR1 and strR2 by merge
+        $visitor->expects($this->at(4))->method('leaveNode')->with($strMiddle)
+                ->will($this->returnValue(array($strR1, $strR2)));
+
+        $traverser = new NodeTraverser;
+        $traverser->addVisitor($visitor);
+
+        $this->assertEquals(
+            array($strStart, $strR1, $strR2, $strEnd),
+            $traverser->traverse(array($strStart, $strMiddle, $strEnd))
+        );
+    }
+
+    public function testDeepArray() {
+        $strNode = new String_('Foo');
+        $stmts = array(array(array($strNode)));
+
+        $visitor = $this->getMock('PhpParser\NodeVisitor');
+        $visitor->expects($this->at(1))->method('enterNode')->with($strNode);
+
+        $traverser = new NodeTraverser;
+        $traverser->addVisitor($visitor);
+
+        $this->assertEquals($stmts, $traverser->traverse($stmts));
+    }
+
+    public function testDontTraverseChildren() {
+        $strNode = new String_('str');
+        $printNode = new Expr\Print_($strNode);
+        $varNode = new Expr\Variable('foo');
+        $mulNode = new Expr\BinaryOp\Mul($varNode, $varNode);
+        $negNode = new Expr\UnaryMinus($mulNode);
+        $stmts = array($printNode, $negNode);
+
+        $visitor1 = $this->getMock('PhpParser\NodeVisitor');
+        $visitor2 = $this->getMock('PhpParser\NodeVisitor');
+
+        $visitor1->expects($this->at(1))->method('enterNode')->with($printNode)
+            ->will($this->returnValue(NodeTraverser::DONT_TRAVERSE_CHILDREN));
+        $visitor2->expects($this->at(1))->method('enterNode')->with($printNode);
+
+        $visitor1->expects($this->at(2))->method('leaveNode')->with($printNode);
+        $visitor2->expects($this->at(2))->method('leaveNode')->with($printNode);
+
+        $visitor1->expects($this->at(3))->method('enterNode')->with($negNode);
+        $visitor2->expects($this->at(3))->method('enterNode')->with($negNode);
+
+        $visitor1->expects($this->at(4))->method('enterNode')->with($mulNode);
+        $visitor2->expects($this->at(4))->method('enterNode')->with($mulNode)
+            ->will($this->returnValue(NodeTraverser::DONT_TRAVERSE_CHILDREN));
+
+        $visitor1->expects($this->at(5))->method('leaveNode')->with($mulNode);
+        $visitor2->expects($this->at(5))->method('leaveNode')->with($mulNode);
+
+        $visitor1->expects($this->at(6))->method('leaveNode')->with($negNode);
+        $visitor2->expects($this->at(6))->method('leaveNode')->with($negNode);
+
+        $traverser = new NodeTraverser;
+        $traverser->addVisitor($visitor1);
+        $traverser->addVisitor($visitor2);
+
+        $this->assertEquals($stmts, $traverser->traverse($stmts));
+    }
+
+    public function testRemovingVisitor() {
+        $visitor1 = $this->getMock('PhpParser\NodeVisitor');
+        $visitor2 = $this->getMock('PhpParser\NodeVisitor');
+        $visitor3 = $this->getMock('PhpParser\NodeVisitor');
+
+        $traverser = new NodeTraverser;
+        $traverser->addVisitor($visitor1);
+        $traverser->addVisitor($visitor2);
+        $traverser->addVisitor($visitor3);
+
+        $preExpected = array($visitor1, $visitor2, $visitor3);
+        $this->assertAttributeSame($preExpected, 'visitors', $traverser, 'The appropriate visitors have not been added');
+
+        $traverser->removeVisitor($visitor2);
+
+        $postExpected = array(0 => $visitor1, 2 => $visitor3);
+        $this->assertAttributeSame($postExpected, 'visitors', $traverser, 'The appropriate visitors are not present after removal');
+    }
+
+    public function testCloneNodes() {
+        $stmts = array(new Node\Stmt\Echo_(array(new String_('Foo'), new String_('Bar'))));
+
+        $traverser = new NodeTraverser(true);
+
+        $this->assertNotSame($stmts, $traverser->traverse($stmts));
+    }
+
+    public function testNoCloneNodesByDefault() {
+        $stmts = array(new Node\Stmt\Echo_(array(new String_('Foo'), new String_('Bar'))));
+
+        $traverser = new NodeTraverser;
+
+        $this->assertSame($stmts, $traverser->traverse($stmts));
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeVisitor/NameResolverTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeVisitor/NameResolverTest.php
new file mode 100644
index 00000000000..ecc443f8fec
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/NodeVisitor/NameResolverTest.php
@@ -0,0 +1,417 @@
+addVisitor(new NameResolver);
+
+        $stmts = $parser->parse($code);
+        $stmts = $traverser->traverse($stmts);
+
+        $this->assertSame(
+            $this->canonicalize($expectedCode),
+            $prettyPrinter->prettyPrint($stmts)
+        );
+    }
+
+    /**
+     * @covers PhpParser\NodeVisitor\NameResolver
+     */
+    public function testResolveLocations() {
+        $code = <<<'EOC'
+addVisitor(new NameResolver);
+
+        $stmts = $parser->parse($code);
+        $stmts = $traverser->traverse($stmts);
+
+        $this->assertSame(
+            $this->canonicalize($expectedCode),
+            $prettyPrinter->prettyPrint($stmts)
+        );
+    }
+
+    public function testNoResolveSpecialName() {
+        $stmts = array(new Node\Expr\New_(new Name('self')));
+
+        $traverser = new PhpParser\NodeTraverser;
+        $traverser->addVisitor(new NameResolver);
+
+        $this->assertEquals($stmts, $traverser->traverse($stmts));
+    }
+
+    public function testAddNamespacedName() {
+        $nsStmts = array(
+            new Stmt\Class_('A'),
+            new Stmt\Interface_('B'),
+            new Stmt\Function_('C'),
+            new Stmt\Const_(array(
+                new Node\Const_('D', new Node\Scalar\LNumber(42))
+            )),
+            new Stmt\Trait_('E'),
+            new Expr\New_(new Stmt\Class_(null)),
+        );
+
+        $traverser = new PhpParser\NodeTraverser;
+        $traverser->addVisitor(new NameResolver);
+
+        $stmts = $traverser->traverse([new Stmt\Namespace_(new Name('NS'), $nsStmts)]);
+        $this->assertSame('NS\\A', (string) $stmts[0]->stmts[0]->namespacedName);
+        $this->assertSame('NS\\B', (string) $stmts[0]->stmts[1]->namespacedName);
+        $this->assertSame('NS\\C', (string) $stmts[0]->stmts[2]->namespacedName);
+        $this->assertSame('NS\\D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
+        $this->assertSame('NS\\E', (string) $stmts[0]->stmts[4]->namespacedName);
+        $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
+
+        $stmts = $traverser->traverse([new Stmt\Namespace_(null, $nsStmts)]);
+        $this->assertSame('A',     (string) $stmts[0]->stmts[0]->namespacedName);
+        $this->assertSame('B',     (string) $stmts[0]->stmts[1]->namespacedName);
+        $this->assertSame('C',     (string) $stmts[0]->stmts[2]->namespacedName);
+        $this->assertSame('D',     (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
+        $this->assertSame('E',     (string) $stmts[0]->stmts[4]->namespacedName);
+        $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
+    }
+
+    /**
+     * @dataProvider provideTestError
+     */
+    public function testError(Node $stmt, $errorMsg) {
+        $this->setExpectedException('PhpParser\Error', $errorMsg);
+
+        $traverser = new PhpParser\NodeTraverser;
+        $traverser->addVisitor(new NameResolver);
+        $traverser->traverse(array($stmt));
+    }
+
+    public function provideTestError() {
+        return array(
+            array(
+                new Stmt\Use_(array(
+                    new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
+                    new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
+                ), Stmt\Use_::TYPE_NORMAL),
+                'Cannot use C\D as B because the name is already in use on line 2'
+            ),
+            array(
+                new Stmt\Use_(array(
+                    new Stmt\UseUse(new Name('a\b'), 'b', 0, array('startLine' => 1)),
+                    new Stmt\UseUse(new Name('c\d'), 'B', 0, array('startLine' => 2)),
+                ), Stmt\Use_::TYPE_FUNCTION),
+                'Cannot use function c\d as B because the name is already in use on line 2'
+            ),
+            array(
+                new Stmt\Use_(array(
+                    new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
+                    new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
+                ), Stmt\Use_::TYPE_CONSTANT),
+                'Cannot use const C\D as B because the name is already in use on line 2'
+            ),
+            array(
+                new Expr\New_(new Name\FullyQualified('self', array('startLine' => 3))),
+                "'\\self' is an invalid class name on line 3"
+            ),
+            array(
+                new Expr\New_(new Name\Relative('self', array('startLine' => 3))),
+                "'\\self' is an invalid class name on line 3"
+            ),
+            array(
+                new Expr\New_(new Name\FullyQualified('PARENT', array('startLine' => 3))),
+                "'\\PARENT' is an invalid class name on line 3"
+            ),
+            array(
+                new Expr\New_(new Name\Relative('STATIC', array('startLine' => 3))),
+                "'\\STATIC' is an invalid class name on line 3"
+            ),
+        );
+    }
+
+    public function testClassNameIsCaseInsensitive()
+    {
+        $source = <<<'EOC'
+parse($source);
+
+        $traverser = new PhpParser\NodeTraverser;
+        $traverser->addVisitor(new NameResolver);
+
+        $stmts = $traverser->traverse($stmts);
+        $stmt = $stmts[0];
+
+        $this->assertSame(array('Bar', 'Baz'), $stmt->stmts[1]->expr->class->parts);
+    }
+
+    public function testSpecialClassNamesAreCaseInsensitive() {
+        $source = <<<'EOC'
+parse($source);
+
+        $traverser = new PhpParser\NodeTraverser;
+        $traverser->addVisitor(new NameResolver);
+
+        $stmts = $traverser->traverse($stmts);
+        $classStmt = $stmts[0];
+        $methodStmt = $classStmt->stmts[0]->stmts[0];
+
+        $this->assertSame('SELF', (string)$methodStmt->stmts[0]->class);
+        $this->assertSame('PARENT', (string)$methodStmt->stmts[1]->class);
+        $this->assertSame('STATIC', (string)$methodStmt->stmts[2]->class);
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Parser/MultipleTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Parser/MultipleTest.php
new file mode 100644
index 00000000000..c6e4dd4fde2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Parser/MultipleTest.php
@@ -0,0 +1,113 @@
+ []]);
+        return new Multiple([new Php7($lexer), new Php5($lexer)]);
+    }
+
+    private function getPrefer5() {
+        $lexer = new Lexer(['usedAttributes' => []]);
+        return new Multiple([new Php5($lexer), new Php7($lexer)]);
+    }
+
+    /** @dataProvider provideTestParse */
+    public function testParse($code, Multiple $parser, $expected) {
+        $this->assertEquals($expected, $parser->parse($code));
+        $this->assertSame([], $parser->getErrors());
+    }
+
+    public function provideTestParse() {
+        return [
+            [
+                // PHP 7 only code
+                'getPrefer5(),
+                [
+                    new Stmt\Class_('Test', ['stmts' => [
+                        new Stmt\ClassMethod('function')
+                    ]]),
+                ]
+            ],
+            [
+                // PHP 5 only code
+                'b;',
+                $this->getPrefer7(),
+                [
+                    new Stmt\Global_([
+                        new Expr\Variable(new Expr\PropertyFetch(new Expr\Variable('a'), 'b'))
+                    ])
+                ]
+            ],
+            [
+                // Different meaning (PHP 5)
+                'getPrefer5(),
+                [
+                    new Expr\Variable(
+                        new Expr\ArrayDimFetch(new Expr\Variable('a'), new LNumber(0))
+                    )
+                ]
+            ],
+            [
+                // Different meaning (PHP 7)
+                'getPrefer7(),
+                [
+                    new Expr\ArrayDimFetch(
+                        new Expr\Variable(new Expr\Variable('a')), new LNumber(0)
+                    )
+                ]
+            ],
+        ];
+    }
+
+    public function testThrownError() {
+        $this->setExpectedException('PhpParser\Error', 'FAIL A');
+
+        $parserA = $this->getMockBuilder('PhpParser\Parser')->getMock();
+        $parserA->expects($this->at(0))
+            ->method('parse')->will($this->throwException(new Error('FAIL A')));
+
+        $parserB = $this->getMockBuilder('PhpParser\Parser')->getMock();
+        $parserB->expects($this->at(0))
+            ->method('parse')->will($this->throwException(new Error('FAIL B')));
+
+        $parser = new Multiple([$parserA, $parserB]);
+        $parser->parse('dummy');
+    }
+
+    public function testGetErrors() {
+        $errorsA = [new Error('A1'), new Error('A2')];
+        $parserA = $this->getMockBuilder('PhpParser\Parser')->getMock();
+        $parserA->expects($this->at(0))->method('parse');
+        $parserA->expects($this->at(1))
+            ->method('getErrors')->will($this->returnValue($errorsA));
+
+        $errorsB = [new Error('B1'), new Error('B2')];
+        $parserB = $this->getMockBuilder('PhpParser\Parser')->getMock();
+        $parserB->expects($this->at(0))->method('parse');
+        $parserB->expects($this->at(1))
+            ->method('getErrors')->will($this->returnValue($errorsB));
+
+        $parser = new Multiple([$parserA, $parserB]);
+        $parser->parse('dummy');
+        $this->assertSame($errorsA, $parser->getErrors());
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Parser/Php5Test.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Parser/Php5Test.php
new file mode 100644
index 00000000000..58c4e617cf3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Parser/Php5Test.php
@@ -0,0 +1,14 @@
+assertInstanceOf($expected, (new ParserFactory)->create($kind, $lexer));
+    }
+
+    public function provideTestCreate() {
+        $lexer = new Lexer();
+        return [
+            [
+                ParserFactory::PREFER_PHP7, $lexer,
+                'PhpParser\Parser\Multiple'
+            ],
+            [
+                ParserFactory::PREFER_PHP5, null,
+                'PhpParser\Parser\Multiple'
+            ],
+            [
+                ParserFactory::ONLY_PHP7, null,
+                'PhpParser\Parser\Php7'
+            ],
+            [
+                ParserFactory::ONLY_PHP5, $lexer,
+                'PhpParser\Parser\Php5'
+            ]
+        ];
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/ParserTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/ParserTest.php
new file mode 100644
index 00000000000..72fd02afe6d
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/ParserTest.php
@@ -0,0 +1,115 @@
+getParser(new Lexer());
+        $parser->parse('getParser(new Lexer());
+        $parser->parse(' array(
+                'comments', 'startLine', 'endLine',
+                'startTokenPos', 'endTokenPos',
+            )
+        ));
+
+        $code = <<<'EOC'
+getParser($lexer);
+        $stmts = $parser->parse($code);
+
+        /** @var \PhpParser\Node\Stmt\Function_ $fn */
+        $fn = $stmts[0];
+        $this->assertInstanceOf('PhpParser\Node\Stmt\Function_', $fn);
+        $this->assertEquals(array(
+            'comments' => array(
+                new Comment\Doc('/** Doc comment */', 2),
+            ),
+            'startLine' => 3,
+            'endLine' => 7,
+            'startTokenPos' => 3,
+            'endTokenPos' => 21,
+        ), $fn->getAttributes());
+
+        $param = $fn->params[0];
+        $this->assertInstanceOf('PhpParser\Node\Param', $param);
+        $this->assertEquals(array(
+            'startLine' => 3,
+            'endLine' => 3,
+            'startTokenPos' => 7,
+            'endTokenPos' => 7,
+        ), $param->getAttributes());
+
+        /** @var \PhpParser\Node\Stmt\Echo_ $echo */
+        $echo = $fn->stmts[0];
+        $this->assertInstanceOf('PhpParser\Node\Stmt\Echo_', $echo);
+        $this->assertEquals(array(
+            'comments' => array(
+                new Comment("// Line\n", 4),
+                new Comment("// Comments\n", 5),
+            ),
+            'startLine' => 6,
+            'endLine' => 6,
+            'startTokenPos' => 16,
+            'endTokenPos' => 19,
+        ), $echo->getAttributes());
+
+        /** @var \PhpParser\Node\Expr\Variable $var */
+        $var = $echo->exprs[0];
+        $this->assertInstanceOf('PhpParser\Node\Expr\Variable', $var);
+        $this->assertEquals(array(
+            'startLine' => 6,
+            'endLine' => 6,
+            'startTokenPos' => 18,
+            'endTokenPos' => 18,
+        ), $var->getAttributes());
+    }
+
+    /**
+     * @expectedException \RangeException
+     * @expectedExceptionMessage The lexer returned an invalid token (id=999, value=foobar)
+     */
+    public function testInvalidToken() {
+        $lexer = new InvalidTokenLexer;
+        $parser = $this->getParser($lexer);
+        $parser->parse('dummy');
+    }
+}
+
+class InvalidTokenLexer extends Lexer {
+    public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
+        $value = 'foobar';
+        return 999;
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/PrettyPrinterTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/PrettyPrinterTest.php
new file mode 100644
index 00000000000..65aa141b661
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/PrettyPrinterTest.php
@@ -0,0 +1,105 @@
+parseModeLine($modeLine);
+        $prettyPrinter = new Standard($options);
+
+        try {
+            $output5 = canonicalize($prettyPrinter->$method($parser5->parse($code)));
+        } catch (Error $e) {
+            $output5 = null;
+            if ('php7' !== $version) {
+                throw $e;
+            }
+        }
+
+        try {
+            $output7 = canonicalize($prettyPrinter->$method($parser7->parse($code)));
+        } catch (Error $e) {
+            $output7 = null;
+            if ('php5' !== $version) {
+                throw $e;
+            }
+        }
+
+        if ('php5' === $version) {
+            $this->assertSame($expected, $output5, $name);
+            $this->assertNotSame($expected, $output7, $name);
+        } else if ('php7' === $version) {
+            $this->assertSame($expected, $output7, $name);
+            $this->assertNotSame($expected, $output5, $name);
+        } else {
+            $this->assertSame($expected, $output5, $name);
+            $this->assertSame($expected, $output7, $name);
+        }
+    }
+
+    /**
+     * @dataProvider provideTestPrettyPrint
+     * @covers PhpParser\PrettyPrinter\Standard
+     */
+    public function testPrettyPrint($name, $code, $expected, $mode) {
+        $this->doTestPrettyPrintMethod('prettyPrint', $name, $code, $expected, $mode);
+    }
+
+    /**
+     * @dataProvider provideTestPrettyPrintFile
+     * @covers PhpParser\PrettyPrinter\Standard
+     */
+    public function testPrettyPrintFile($name, $code, $expected, $mode) {
+        $this->doTestPrettyPrintMethod('prettyPrintFile', $name, $code, $expected, $mode);
+    }
+
+    public function provideTestPrettyPrint() {
+        return $this->getTests(__DIR__ . '/../code/prettyPrinter', 'test');
+    }
+
+    public function provideTestPrettyPrintFile() {
+        return $this->getTests(__DIR__ . '/../code/prettyPrinter', 'file-test');
+    }
+
+    public function testPrettyPrintExpr() {
+        $prettyPrinter = new Standard;
+        $expr = new Expr\BinaryOp\Mul(
+            new Expr\BinaryOp\Plus(new Expr\Variable('a'), new Expr\Variable('b')),
+            new Expr\Variable('c')
+        );
+        $this->assertEquals('($a + $b) * $c', $prettyPrinter->prettyPrintExpr($expr));
+
+        $expr = new Expr\Closure(array(
+            'stmts' => array(new Stmt\Return_(new String_("a\nb")))
+        ));
+        $this->assertEquals("function () {\n    return 'a\nb';\n}", $prettyPrinter->prettyPrintExpr($expr));
+    }
+
+    public function testCommentBeforeInlineHTML() {
+        $prettyPrinter = new PrettyPrinter\Standard;
+        $comment = new Comment\Doc("/**\n * This is a comment\n */");
+        $stmts = [new Stmt\InlineHTML('Hello World!', ['comments' => [$comment]])];
+        $expected = "\nHello World!";
+        $this->assertSame($expected, $prettyPrinter->prettyPrintFile($stmts));
+    }
+
+    private function parseModeLine($modeLine) {
+        $parts = explode(' ', $modeLine, 2);
+        $version = isset($parts[0]) ? $parts[0] : 'both';
+        $options = isset($parts[1]) ? json_decode($parts[1], true) : [];
+        return [$version, $options];
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Serializer/XMLTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Serializer/XMLTest.php
new file mode 100644
index 00000000000..256b271c692
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Serializer/XMLTest.php
@@ -0,0 +1,166 @@
+
+     */
+    public function testSerialize() {
+        $code = <<
+
+ 
+  
+   
+    
+     // comment
+
+     /** doc comment */
+    
+   
+   
+    4
+   
+   
+    6
+   
+   
+    
+   
+   
+    functionName
+   
+   
+    
+     
+      
+       4
+      
+      
+       4
+      
+      
+       
+      
+      
+       
+      
+      
+       
+      
+      
+       a
+      
+      
+       
+        
+         4
+        
+        
+         4
+        
+        
+         0
+        
+       
+      
+     
+     
+      
+       4
+      
+      
+       4
+      
+      
+       
+      
+      
+       
+      
+      
+       
+      
+      
+       b
+      
+      
+       
+        
+         4
+        
+        
+         4
+        
+        
+         1
+        
+       
+      
+     
+    
+   
+   
+    
+   
+   
+    
+     
+      
+       5
+      
+      
+       5
+      
+      
+       
+        
+         
+          5
+         
+         
+          5
+         
+         
+          Foo
+         
+        
+       
+      
+     
+    
+   
+  
+ 
+
+XML;
+
+        $parser     = new PhpParser\Parser\Php7(new PhpParser\Lexer);
+        $serializer = new XML;
+
+        $code = str_replace("\r\n", "\n", $code);
+        $stmts = $parser->parse($code);
+        $this->assertXmlStringEqualsXmlString($xml, $serializer->serialize($stmts));
+    }
+
+    /**
+     * @expectedException        \InvalidArgumentException
+     * @expectedExceptionMessage Unexpected node type
+     */
+    public function testError() {
+        $serializer = new XML;
+        $serializer->serialize(array(new \stdClass));
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Unserializer/XMLTest.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Unserializer/XMLTest.php
new file mode 100644
index 00000000000..8ee5d7b57ef
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/PhpParser/Unserializer/XMLTest.php
@@ -0,0 +1,150 @@
+
+
+ 
+  
+   1
+  
+  
+   
+    // comment
+
+    /** doc comment */
+   
+  
+  
+   Test
+  
+ 
+
+XML;
+
+        $unserializer  = new XML;
+        $this->assertEquals(
+            new Scalar\String_('Test', array(
+                'startLine' => 1,
+                'comments'  => array(
+                    new Comment('// comment' . "\n", 2),
+                    new Comment\Doc('/** doc comment */', 3),
+                ),
+            )),
+            $unserializer->unserialize($xml)
+        );
+    }
+
+    public function testEmptyNode() {
+        $xml = <<
+
+ 
+
+XML;
+
+        $unserializer  = new XML;
+
+        $this->assertEquals(
+            new Scalar\MagicConst\Class_,
+            $unserializer->unserialize($xml)
+        );
+    }
+
+    public function testScalars() {
+        $xml = <<
+
+ 
+  
+  
+  test
+  
+  
+  1
+  1
+  1.5
+  
+  
+  
+ 
+
+XML;
+        $result = array(
+            array(), array(),
+            'test', '', '',
+            1,
+            1, 1.5,
+            true, false, null
+        );
+
+        $unserializer  = new XML;
+        $this->assertEquals($result, $unserializer->unserialize($xml));
+    }
+
+    /**
+     * @expectedException        \DomainException
+     * @expectedExceptionMessage AST root element not found
+     */
+    public function testWrongRootElementError() {
+        $xml = <<
+
+XML;
+
+        $unserializer = new XML;
+        $unserializer->unserialize($xml);
+    }
+
+    /**
+     * @dataProvider             provideTestErrors
+     */
+    public function testErrors($xml, $errorMsg) {
+        $this->setExpectedException('DomainException', $errorMsg);
+
+        $xml = <<
+
+ $xml
+
+XML;
+
+        $unserializer = new XML;
+        $unserializer->unserialize($xml);
+    }
+
+    public function provideTestErrors() {
+        return array(
+            array('test',   '"true" scalar must be empty'),
+            array('test', '"false" scalar must be empty'),
+            array('test',   '"null" scalar must be empty'),
+            array('bar',      'Unknown scalar type "foo"'),
+            array('x',        '"x" is not a valid int'),
+            array('x',    '"x" is not a valid float'),
+            array('',                                  'Expected node or scalar'),
+            array('test',           'Unexpected node of type "foo:bar"'),
+            array(
+                'test',
+                'Expected sub node or attribute, got node of type "foo:bar"'
+            ),
+            array(
+                '',
+                'Expected node or scalar'
+            ),
+            array(
+                '',
+                'Unknown node type "Foo"'
+            ),
+        );
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/bootstrap.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/bootstrap.php
new file mode 100644
index 00000000000..1afd200ad3b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/bootstrap.php
@@ -0,0 +1,16 @@
+ 'd', 'e' => &$f);
+
+// short array syntax
+[];
+[1, 2, 3];
+['a' => 'b'];
+-----
+array(
+    0: Expr_Array(
+        items: array(
+        )
+    )
+    1: Expr_Array(
+        items: array(
+            0: Expr_ArrayItem(
+                key: null
+                value: Scalar_String(
+                    value: a
+                )
+                byRef: false
+            )
+        )
+    )
+    2: Expr_Array(
+        items: array(
+            0: Expr_ArrayItem(
+                key: null
+                value: Scalar_String(
+                    value: a
+                )
+                byRef: false
+            )
+        )
+    )
+    3: Expr_Array(
+        items: array(
+            0: Expr_ArrayItem(
+                key: null
+                value: Scalar_String(
+                    value: a
+                )
+                byRef: false
+            )
+            1: Expr_ArrayItem(
+                key: null
+                value: Scalar_String(
+                    value: b
+                )
+                byRef: false
+            )
+        )
+    )
+    4: Expr_Array(
+        items: array(
+            0: Expr_ArrayItem(
+                key: null
+                value: Scalar_String(
+                    value: a
+                )
+                byRef: false
+            )
+            1: Expr_ArrayItem(
+                key: null
+                value: Expr_Variable(
+                    name: b
+                )
+                byRef: true
+            )
+            2: Expr_ArrayItem(
+                key: Scalar_String(
+                    value: c
+                )
+                value: Scalar_String(
+                    value: d
+                )
+                byRef: false
+            )
+            3: Expr_ArrayItem(
+                key: Scalar_String(
+                    value: e
+                )
+                value: Expr_Variable(
+                    name: f
+                )
+                byRef: true
+            )
+        )
+    )
+    5: Expr_Array(
+        items: array(
+        )
+    )
+    6: Expr_Array(
+        items: array(
+            0: Expr_ArrayItem(
+                key: null
+                value: Scalar_LNumber(
+                    value: 1
+                )
+                byRef: false
+            )
+            1: Expr_ArrayItem(
+                key: null
+                value: Scalar_LNumber(
+                    value: 2
+                )
+                byRef: false
+            )
+            2: Expr_ArrayItem(
+                key: null
+                value: Scalar_LNumber(
+                    value: 3
+                )
+                byRef: false
+            )
+        )
+    )
+    7: Expr_Array(
+        items: array(
+            0: Expr_ArrayItem(
+                key: Scalar_String(
+                    value: a
+                )
+                value: Scalar_String(
+                    value: b
+                )
+                byRef: false
+            )
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/assign.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/assign.test
new file mode 100644
index 00000000000..66ae18f498f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/assign.test
@@ -0,0 +1,255 @@
+Assignments
+-----
+>= $b;
+$a **= $b;
+
+// chained assign
+$a = $b *= $c **= $d;
+
+// by ref assign
+$a =& $b;
+$a =& new B;
+
+// list() assign
+list($a) = $b;
+list($a, , $b) = $c;
+list($a, list(, $c), $d) = $e;
+
+// inc/dec
+++$a;
+$a++;
+--$a;
+$a--;
+-----
+array(
+    0: Expr_Assign(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    1: Expr_AssignOp_BitwiseAnd(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    2: Expr_AssignOp_BitwiseOr(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    3: Expr_AssignOp_BitwiseXor(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    4: Expr_AssignOp_Concat(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    5: Expr_AssignOp_Div(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    6: Expr_AssignOp_Minus(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    7: Expr_AssignOp_Mod(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    8: Expr_AssignOp_Mul(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    9: Expr_AssignOp_Plus(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    10: Expr_AssignOp_ShiftLeft(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    11: Expr_AssignOp_ShiftRight(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    12: Expr_AssignOp_Pow(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    13: Expr_Assign(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_AssignOp_Mul(
+            var: Expr_Variable(
+                name: b
+            )
+            expr: Expr_AssignOp_Pow(
+                var: Expr_Variable(
+                    name: c
+                )
+                expr: Expr_Variable(
+                    name: d
+                )
+            )
+        )
+    )
+    14: Expr_AssignRef(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    15: Expr_AssignRef(
+        var: Expr_Variable(
+            name: a
+        )
+        expr: Expr_New(
+            class: Name(
+                parts: array(
+                    0: B
+                )
+            )
+            args: array(
+            )
+        )
+    )
+    16: Expr_Assign(
+        var: Expr_List(
+            vars: array(
+                0: Expr_Variable(
+                    name: a
+                )
+            )
+        )
+        expr: Expr_Variable(
+            name: b
+        )
+    )
+    17: Expr_Assign(
+        var: Expr_List(
+            vars: array(
+                0: Expr_Variable(
+                    name: a
+                )
+                1: null
+                2: Expr_Variable(
+                    name: b
+                )
+            )
+        )
+        expr: Expr_Variable(
+            name: c
+        )
+    )
+    18: Expr_Assign(
+        var: Expr_List(
+            vars: array(
+                0: Expr_Variable(
+                    name: a
+                )
+                1: Expr_List(
+                    vars: array(
+                        0: null
+                        1: Expr_Variable(
+                            name: c
+                        )
+                    )
+                )
+                2: Expr_Variable(
+                    name: d
+                )
+            )
+        )
+        expr: Expr_Variable(
+            name: e
+        )
+    )
+    19: Expr_PreInc(
+        var: Expr_Variable(
+            name: a
+        )
+    )
+    20: Expr_PostInc(
+        var: Expr_Variable(
+            name: a
+        )
+    )
+    21: Expr_PreDec(
+        var: Expr_Variable(
+            name: a
+        )
+    )
+    22: Expr_PostDec(
+        var: Expr_Variable(
+            name: a
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/cast.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/cast.test
new file mode 100644
index 00000000000..3c54ba72d35
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/cast.test
@@ -0,0 +1,72 @@
+Casts
+-----
+ $b;
+$a >= $b;
+$a == $b;
+$a === $b;
+$a != $b;
+$a !== $b;
+$a <=> $b;
+$a instanceof B;
+$a instanceof $b;
+-----
+array(
+    0: Expr_BinaryOp_Smaller(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    1: Expr_BinaryOp_SmallerOrEqual(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    2: Expr_BinaryOp_Greater(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    3: Expr_BinaryOp_GreaterOrEqual(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    4: Expr_BinaryOp_Equal(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    5: Expr_BinaryOp_Identical(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    6: Expr_BinaryOp_NotEqual(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    7: Expr_BinaryOp_NotIdentical(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    8: Expr_BinaryOp_Spaceship(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    9: Expr_Instanceof(
+        expr: Expr_Variable(
+            name: a
+        )
+        class: Name(
+            parts: array(
+                0: B
+            )
+        )
+    )
+    10: Expr_Instanceof(
+        expr: Expr_Variable(
+            name: a
+        )
+        class: Expr_Variable(
+            name: b
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/constant_expr.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/constant_expr.test
new file mode 100644
index 00000000000..3e7a23ef0d9
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/constant_expr.test
@@ -0,0 +1,621 @@
+Expressions in static scalar context
+-----
+ 0;
+const T_20 = 1 >= 0;
+const T_21 = 1 === 1;
+const T_22 = 1 !== 1;
+const T_23 = 0 != "0";
+const T_24 = 1 == "1";
+const T_25 = 1 + 2 * 3;
+const T_26 = "1" + 2 + "3";
+const T_27 = 2 ** 3;
+const T_28 = [1, 2, 3][1];
+const T_29 = 12 - 13;
+const T_30 = 12 ^ 13;
+const T_31 = 12 & 13;
+const T_32 = 12 | 13;
+const T_33 = 12 % 3;
+const T_34 = 100 >> 4;
+const T_35 = !false;
+-----
+array(
+    0: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_1
+                value: Expr_BinaryOp_ShiftLeft(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 1
+                    )
+                )
+            )
+        )
+    )
+    1: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_2
+                value: Expr_BinaryOp_Div(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 2
+                    )
+                )
+            )
+        )
+    )
+    2: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_3
+                value: Expr_BinaryOp_Plus(
+                    left: Scalar_DNumber(
+                        value: 1.5
+                    )
+                    right: Scalar_DNumber(
+                        value: 1.5
+                    )
+                )
+            )
+        )
+    )
+    3: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_4
+                value: Expr_BinaryOp_Concat(
+                    left: Scalar_String(
+                        value: foo
+                    )
+                    right: Scalar_String(
+                        value: bar
+                    )
+                )
+            )
+        )
+    )
+    4: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_5
+                value: Expr_BinaryOp_Mul(
+                    left: Expr_BinaryOp_Plus(
+                        left: Scalar_DNumber(
+                            value: 1.5
+                        )
+                        right: Scalar_DNumber(
+                            value: 1.5
+                        )
+                    )
+                    right: Scalar_LNumber(
+                        value: 2
+                    )
+                )
+            )
+        )
+    )
+    5: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_6
+                value: Expr_BinaryOp_Concat(
+                    left: Expr_BinaryOp_Concat(
+                        left: Expr_BinaryOp_Concat(
+                            left: Scalar_String(
+                                value: foo
+                            )
+                            right: Scalar_LNumber(
+                                value: 2
+                            )
+                        )
+                        right: Scalar_LNumber(
+                            value: 3
+                        )
+                    )
+                    right: Scalar_DNumber(
+                        value: 4
+                    )
+                )
+            )
+        )
+    )
+    6: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_7
+                value: Scalar_MagicConst_Line(
+                )
+            )
+        )
+    )
+    7: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_8
+                value: Scalar_String(
+                    value: This is a test string
+                )
+            )
+        )
+    )
+    8: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_9
+                value: Expr_BitwiseNot(
+                    expr: Expr_UnaryMinus(
+                        expr: Scalar_LNumber(
+                            value: 1
+                        )
+                    )
+                )
+            )
+        )
+    )
+    9: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_10
+                value: Expr_BinaryOp_Plus(
+                    left: Expr_Ternary(
+                        cond: Expr_UnaryMinus(
+                            expr: Scalar_LNumber(
+                                value: 1
+                            )
+                        )
+                        if: null
+                        else: Scalar_LNumber(
+                            value: 1
+                        )
+                    )
+                    right: Expr_Ternary(
+                        cond: Scalar_LNumber(
+                            value: 0
+                        )
+                        if: Scalar_LNumber(
+                            value: 2
+                        )
+                        else: Scalar_LNumber(
+                            value: 3
+                        )
+                    )
+                )
+            )
+        )
+    )
+    10: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_11
+                value: Expr_BinaryOp_BooleanAnd(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    11: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_12
+                value: Expr_BinaryOp_LogicalAnd(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 1
+                    )
+                )
+            )
+        )
+    )
+    12: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_13
+                value: Expr_BinaryOp_BooleanOr(
+                    left: Scalar_LNumber(
+                        value: 0
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    13: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_14
+                value: Expr_BinaryOp_LogicalOr(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    14: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_15
+                value: Expr_BinaryOp_LogicalXor(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 1
+                    )
+                )
+            )
+        )
+    )
+    15: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_16
+                value: Expr_BinaryOp_LogicalXor(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    16: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_17
+                value: Expr_BinaryOp_Smaller(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    17: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_18
+                value: Expr_BinaryOp_SmallerOrEqual(
+                    left: Scalar_LNumber(
+                        value: 0
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    18: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_19
+                value: Expr_BinaryOp_Greater(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    19: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_20
+                value: Expr_BinaryOp_GreaterOrEqual(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    20: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_21
+                value: Expr_BinaryOp_Identical(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 1
+                    )
+                )
+            )
+        )
+    )
+    21: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_22
+                value: Expr_BinaryOp_NotIdentical(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_LNumber(
+                        value: 1
+                    )
+                )
+            )
+        )
+    )
+    22: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_23
+                value: Expr_BinaryOp_NotEqual(
+                    left: Scalar_LNumber(
+                        value: 0
+                    )
+                    right: Scalar_String(
+                        value: 0
+                    )
+                )
+            )
+        )
+    )
+    23: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_24
+                value: Expr_BinaryOp_Equal(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Scalar_String(
+                        value: 1
+                    )
+                )
+            )
+        )
+    )
+    24: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_25
+                value: Expr_BinaryOp_Plus(
+                    left: Scalar_LNumber(
+                        value: 1
+                    )
+                    right: Expr_BinaryOp_Mul(
+                        left: Scalar_LNumber(
+                            value: 2
+                        )
+                        right: Scalar_LNumber(
+                            value: 3
+                        )
+                    )
+                )
+            )
+        )
+    )
+    25: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_26
+                value: Expr_BinaryOp_Plus(
+                    left: Expr_BinaryOp_Plus(
+                        left: Scalar_String(
+                            value: 1
+                        )
+                        right: Scalar_LNumber(
+                            value: 2
+                        )
+                    )
+                    right: Scalar_String(
+                        value: 3
+                    )
+                )
+            )
+        )
+    )
+    26: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_27
+                value: Expr_BinaryOp_Pow(
+                    left: Scalar_LNumber(
+                        value: 2
+                    )
+                    right: Scalar_LNumber(
+                        value: 3
+                    )
+                )
+            )
+        )
+    )
+    27: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_28
+                value: Expr_ArrayDimFetch(
+                    var: Expr_Array(
+                        items: array(
+                            0: Expr_ArrayItem(
+                                key: null
+                                value: Scalar_LNumber(
+                                    value: 1
+                                )
+                                byRef: false
+                            )
+                            1: Expr_ArrayItem(
+                                key: null
+                                value: Scalar_LNumber(
+                                    value: 2
+                                )
+                                byRef: false
+                            )
+                            2: Expr_ArrayItem(
+                                key: null
+                                value: Scalar_LNumber(
+                                    value: 3
+                                )
+                                byRef: false
+                            )
+                        )
+                    )
+                    dim: Scalar_LNumber(
+                        value: 1
+                    )
+                )
+            )
+        )
+    )
+    28: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_29
+                value: Expr_BinaryOp_Minus(
+                    left: Scalar_LNumber(
+                        value: 12
+                    )
+                    right: Scalar_LNumber(
+                        value: 13
+                    )
+                )
+            )
+        )
+    )
+    29: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_30
+                value: Expr_BinaryOp_BitwiseXor(
+                    left: Scalar_LNumber(
+                        value: 12
+                    )
+                    right: Scalar_LNumber(
+                        value: 13
+                    )
+                )
+            )
+        )
+    )
+    30: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_31
+                value: Expr_BinaryOp_BitwiseAnd(
+                    left: Scalar_LNumber(
+                        value: 12
+                    )
+                    right: Scalar_LNumber(
+                        value: 13
+                    )
+                )
+            )
+        )
+    )
+    31: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_32
+                value: Expr_BinaryOp_BitwiseOr(
+                    left: Scalar_LNumber(
+                        value: 12
+                    )
+                    right: Scalar_LNumber(
+                        value: 13
+                    )
+                )
+            )
+        )
+    )
+    32: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_33
+                value: Expr_BinaryOp_Mod(
+                    left: Scalar_LNumber(
+                        value: 12
+                    )
+                    right: Scalar_LNumber(
+                        value: 3
+                    )
+                )
+            )
+        )
+    )
+    33: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_34
+                value: Expr_BinaryOp_ShiftRight(
+                    left: Scalar_LNumber(
+                        value: 100
+                    )
+                    right: Scalar_LNumber(
+                        value: 4
+                    )
+                )
+            )
+        )
+    )
+    34: Stmt_Const(
+        consts: array(
+            0: Const(
+                name: T_35
+                value: Expr_BooleanNot(
+                    expr: Expr_ConstFetch(
+                        name: Name(
+                            parts: array(
+                                0: false
+                            )
+                        )
+                    )
+                )
+            )
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/errorSuppress.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/errorSuppress.test
new file mode 100644
index 00000000000..ce3fce96a22
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/errorSuppress.test
@@ -0,0 +1,12 @@
+Error suppression
+-----
+b['c']();
+
+// array dereferencing
+a()['b'];
+-----
+array(
+    0: Expr_FuncCall(
+        name: Name(
+            parts: array(
+                0: a
+            )
+        )
+        args: array(
+        )
+    )
+    1: Expr_FuncCall(
+        name: Expr_Variable(
+            name: a
+        )
+        args: array(
+        )
+    )
+    2: Expr_FuncCall(
+        name: Expr_Variable(
+            name: Scalar_String(
+                value: a
+            )
+        )
+        args: array(
+        )
+    )
+    3: Expr_FuncCall(
+        name: Expr_Variable(
+            name: Expr_Variable(
+                name: a
+            )
+        )
+        args: array(
+        )
+    )
+    4: Expr_FuncCall(
+        name: Expr_Variable(
+            name: Expr_Variable(
+                name: Expr_Variable(
+                    name: a
+                )
+            )
+        )
+        args: array(
+        )
+    )
+    5: Expr_FuncCall(
+        name: Expr_ArrayDimFetch(
+            var: Expr_Variable(
+                name: a
+            )
+            dim: Scalar_String(
+                value: b
+            )
+        )
+        args: array(
+        )
+    )
+    6: Expr_FuncCall(
+        name: Expr_ArrayDimFetch(
+            var: Expr_Variable(
+                name: a
+            )
+            dim: Scalar_String(
+                value: b
+            )
+        )
+        args: array(
+        )
+    )
+    7: Expr_FuncCall(
+        name: Expr_ArrayDimFetch(
+            var: Expr_PropertyFetch(
+                var: Expr_Variable(
+                    name: a
+                )
+                name: b
+            )
+            dim: Scalar_String(
+                value: c
+            )
+        )
+        args: array(
+        )
+    )
+    8: Expr_ArrayDimFetch(
+        var: Expr_FuncCall(
+            name: Name(
+                parts: array(
+                    0: a
+                )
+            )
+            args: array(
+            )
+        )
+        dim: Scalar_String(
+            value: b
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/newDeref.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/newDeref.test
new file mode 100644
index 00000000000..5e36ff81023
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/newDeref.test
@@ -0,0 +1,70 @@
+New expression dereferencing
+-----
+b;
+(new A)->b();
+(new A)['b'];
+(new A)['b']['c'];
+-----
+array(
+    0: Expr_PropertyFetch(
+        var: Expr_New(
+            class: Name(
+                parts: array(
+                    0: A
+                )
+            )
+            args: array(
+            )
+        )
+        name: b
+    )
+    1: Expr_MethodCall(
+        var: Expr_New(
+            class: Name(
+                parts: array(
+                    0: A
+                )
+            )
+            args: array(
+            )
+        )
+        name: b
+        args: array(
+        )
+    )
+    2: Expr_ArrayDimFetch(
+        var: Expr_New(
+            class: Name(
+                parts: array(
+                    0: A
+                )
+            )
+            args: array(
+            )
+        )
+        dim: Scalar_String(
+            value: b
+        )
+    )
+    3: Expr_ArrayDimFetch(
+        var: Expr_ArrayDimFetch(
+            var: Expr_New(
+                class: Name(
+                    parts: array(
+                        0: A
+                    )
+                )
+                args: array(
+                )
+            )
+            dim: Scalar_String(
+                value: b
+            )
+        )
+        dim: Scalar_String(
+            value: c
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/objectAccess.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/objectAccess.test
new file mode 100644
index 00000000000..74f8080bd76
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/objectAccess.test
@@ -0,0 +1,119 @@
+Object access
+-----
+b;
+$a->b['c'];
+$a->b{'c'};
+
+// method call variations
+$a->b();
+$a->{'b'}();
+$a->$b();
+$a->$b['c']();
+
+// array dereferencing
+$a->b()['c'];
+$a->b(){'c'}; // invalid PHP: drop Support?
+-----
+!!php5
+array(
+    0: Expr_PropertyFetch(
+        var: Expr_Variable(
+            name: a
+        )
+        name: b
+    )
+    1: Expr_ArrayDimFetch(
+        var: Expr_PropertyFetch(
+            var: Expr_Variable(
+                name: a
+            )
+            name: b
+        )
+        dim: Scalar_String(
+            value: c
+        )
+    )
+    2: Expr_ArrayDimFetch(
+        var: Expr_PropertyFetch(
+            var: Expr_Variable(
+                name: a
+            )
+            name: b
+        )
+        dim: Scalar_String(
+            value: c
+        )
+    )
+    3: Expr_MethodCall(
+        var: Expr_Variable(
+            name: a
+        )
+        name: b
+        args: array(
+        )
+    )
+    4: Expr_MethodCall(
+        var: Expr_Variable(
+            name: a
+        )
+        name: Scalar_String(
+            value: b
+        )
+        args: array(
+        )
+    )
+    5: Expr_MethodCall(
+        var: Expr_Variable(
+            name: a
+        )
+        name: Expr_Variable(
+            name: b
+        )
+        args: array(
+        )
+    )
+    6: Expr_MethodCall(
+        var: Expr_Variable(
+            name: a
+        )
+        name: Expr_ArrayDimFetch(
+            var: Expr_Variable(
+                name: b
+            )
+            dim: Scalar_String(
+                value: c
+            )
+        )
+        args: array(
+        )
+    )
+    7: Expr_ArrayDimFetch(
+        var: Expr_MethodCall(
+            var: Expr_Variable(
+                name: a
+            )
+            name: b
+            args: array(
+            )
+        )
+        dim: Scalar_String(
+            value: c
+        )
+    )
+    8: Expr_ArrayDimFetch(
+        var: Expr_MethodCall(
+            var: Expr_Variable(
+                name: a
+            )
+            name: b
+            args: array(
+            )
+        )
+        dim: Scalar_String(
+            value: c
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/simpleArrayAccess.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/simpleArrayAccess.test
new file mode 100644
index 00000000000..ea3f9ef4311
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/fetchAndCall/simpleArrayAccess.test
@@ -0,0 +1,62 @@
+Simple array access
+-----
+> $b;
+$a ** $b;
+
+// associativity
+$a * $b * $c;
+$a * ($b * $c);
+
+// precedence
+$a + $b * $c;
+($a + $b) * $c;
+
+// pow is special
+$a ** $b ** $c;
+($a ** $b) ** $c;
+-----
+array(
+    0: Expr_BitwiseNot(
+        expr: Expr_Variable(
+            name: a
+        )
+    )
+    1: Expr_UnaryPlus(
+        expr: Expr_Variable(
+            name: a
+        )
+    )
+    2: Expr_UnaryMinus(
+        expr: Expr_Variable(
+            name: a
+        )
+    )
+    3: Expr_BinaryOp_BitwiseAnd(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    4: Expr_BinaryOp_BitwiseOr(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    5: Expr_BinaryOp_BitwiseXor(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    6: Expr_BinaryOp_Concat(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    7: Expr_BinaryOp_Div(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    8: Expr_BinaryOp_Minus(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    9: Expr_BinaryOp_Mod(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    10: Expr_BinaryOp_Mul(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    11: Expr_BinaryOp_Plus(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    12: Expr_BinaryOp_ShiftLeft(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    13: Expr_BinaryOp_ShiftRight(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    14: Expr_BinaryOp_Pow(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_Variable(
+            name: b
+        )
+    )
+    15: Expr_BinaryOp_Mul(
+        left: Expr_BinaryOp_Mul(
+            left: Expr_Variable(
+                name: a
+            )
+            right: Expr_Variable(
+                name: b
+            )
+        )
+        right: Expr_Variable(
+            name: c
+        )
+    )
+    16: Expr_BinaryOp_Mul(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_BinaryOp_Mul(
+            left: Expr_Variable(
+                name: b
+            )
+            right: Expr_Variable(
+                name: c
+            )
+        )
+    )
+    17: Expr_BinaryOp_Plus(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_BinaryOp_Mul(
+            left: Expr_Variable(
+                name: b
+            )
+            right: Expr_Variable(
+                name: c
+            )
+        )
+    )
+    18: Expr_BinaryOp_Mul(
+        left: Expr_BinaryOp_Plus(
+            left: Expr_Variable(
+                name: a
+            )
+            right: Expr_Variable(
+                name: b
+            )
+        )
+        right: Expr_Variable(
+            name: c
+        )
+    )
+    19: Expr_BinaryOp_Pow(
+        left: Expr_Variable(
+            name: a
+        )
+        right: Expr_BinaryOp_Pow(
+            left: Expr_Variable(
+                name: b
+            )
+            right: Expr_Variable(
+                name: c
+            )
+        )
+    )
+    20: Expr_BinaryOp_Pow(
+        left: Expr_BinaryOp_Pow(
+            left: Expr_Variable(
+                name: a
+            )
+            right: Expr_Variable(
+                name: b
+            )
+        )
+        right: Expr_Variable(
+            name: c
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/new.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/new.test
new file mode 100644
index 00000000000..b7ce7a9d401
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/new.test
@@ -0,0 +1,140 @@
+New
+-----
+b();
+new $a->b->c();
+new $a->b['c']();
+new $a->b{'c'}();
+
+// test regression introduces by new dereferencing syntax
+(new A);
+-----
+array(
+    0: Expr_New(
+        class: Name(
+            parts: array(
+                0: A
+            )
+        )
+        args: array(
+        )
+    )
+    1: Expr_New(
+        class: Name(
+            parts: array(
+                0: A
+            )
+        )
+        args: array(
+            0: Arg(
+                value: Expr_Variable(
+                    name: b
+                )
+                byRef: false
+                unpack: false
+            )
+        )
+    )
+    2: Expr_New(
+        class: Expr_Variable(
+            name: a
+        )
+        args: array(
+        )
+    )
+    3: Expr_New(
+        class: Expr_ArrayDimFetch(
+            var: Expr_Variable(
+                name: a
+            )
+            dim: Scalar_String(
+                value: b
+            )
+        )
+        args: array(
+        )
+    )
+    4: Expr_New(
+        class: Expr_StaticPropertyFetch(
+            class: Name(
+                parts: array(
+                    0: A
+                )
+            )
+            name: b
+        )
+        args: array(
+        )
+    )
+    5: Expr_New(
+        class: Expr_PropertyFetch(
+            var: Expr_Variable(
+                name: a
+            )
+            name: b
+        )
+        args: array(
+        )
+    )
+    6: Expr_New(
+        class: Expr_PropertyFetch(
+            var: Expr_PropertyFetch(
+                var: Expr_Variable(
+                    name: a
+                )
+                name: b
+            )
+            name: c
+        )
+        args: array(
+        )
+    )
+    7: Expr_New(
+        class: Expr_ArrayDimFetch(
+            var: Expr_PropertyFetch(
+                var: Expr_Variable(
+                    name: a
+                )
+                name: b
+            )
+            dim: Scalar_String(
+                value: c
+            )
+        )
+        args: array(
+        )
+    )
+    8: Expr_New(
+        class: Expr_ArrayDimFetch(
+            var: Expr_PropertyFetch(
+                var: Expr_Variable(
+                    name: a
+                )
+                name: b
+            )
+            dim: Scalar_String(
+                value: c
+            )
+        )
+        args: array(
+        )
+    )
+    9: Expr_New(
+        class: Name(
+            parts: array(
+                0: A
+            )
+        )
+        args: array(
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/newWithoutClass.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/newWithoutClass.test
new file mode 100644
index 00000000000..84ec7b1745b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/newWithoutClass.test
@@ -0,0 +1,8 @@
+New without a class
+-----
+bar;
+-----
+!!php7
+Syntax error, unexpected T_OBJECT_OPERATOR, expecting ',' or ';' from 2:13 to 2:14
+array(
+    0: Expr_ConstFetch(
+        name: Name(
+            parts: array(
+                0: bar
+            )
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/indirectCall.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/indirectCall.test
new file mode 100644
index 00000000000..bb3e7fbead3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/indirectCall.test
@@ -0,0 +1,481 @@
+UVS indirect calls
+-----
+ 'b']->a);
+isset("str"->a);
+-----
+!!php7
+array(
+    0: Expr_Isset(
+        vars: array(
+            0: Expr_ArrayDimFetch(
+                var: Expr_BinaryOp_Plus(
+                    left: Expr_Array(
+                        items: array(
+                            0: Expr_ArrayItem(
+                                key: null
+                                value: Scalar_LNumber(
+                                    value: 0
+                                )
+                                byRef: false
+                            )
+                            1: Expr_ArrayItem(
+                                key: null
+                                value: Scalar_LNumber(
+                                    value: 1
+                                )
+                                byRef: false
+                            )
+                        )
+                    )
+                    right: Expr_Array(
+                        items: array(
+                        )
+                    )
+                )
+                dim: Scalar_LNumber(
+                    value: 0
+                )
+            )
+        )
+    )
+    1: Expr_Isset(
+        vars: array(
+            0: Expr_PropertyFetch(
+                var: Expr_Array(
+                    items: array(
+                        0: Expr_ArrayItem(
+                            key: Scalar_String(
+                                value: a
+                            )
+                            value: Scalar_String(
+                                value: b
+                            )
+                            byRef: false
+                        )
+                    )
+                )
+                name: a
+            )
+        )
+    )
+    2: Expr_Isset(
+        vars: array(
+            0: Expr_PropertyFetch(
+                var: Scalar_String(
+                    value: str
+                )
+                name: a
+            )
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/misc.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/misc.test
new file mode 100644
index 00000000000..2c5ba900ec5
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/misc.test
@@ -0,0 +1,109 @@
+Uniform variable syntax in PHP 7 (misc)
+-----
+length();
+(clone $obj)->b[0](1);
+[0, 1][0] = 1;
+-----
+!!php7
+array(
+    0: Expr_ArrayDimFetch(
+        var: Expr_ClassConstFetch(
+            class: Name(
+                parts: array(
+                    0: A
+                )
+            )
+            name: A
+        )
+        dim: Scalar_LNumber(
+            value: 0
+        )
+    )
+    1: Expr_ArrayDimFetch(
+        var: Expr_ArrayDimFetch(
+            var: Expr_ArrayDimFetch(
+                var: Expr_ClassConstFetch(
+                    class: Name(
+                        parts: array(
+                            0: A
+                        )
+                    )
+                    name: A
+                )
+                dim: Scalar_LNumber(
+                    value: 0
+                )
+            )
+            dim: Scalar_LNumber(
+                value: 1
+            )
+        )
+        dim: Scalar_LNumber(
+            value: 2
+        )
+    )
+    2: Expr_MethodCall(
+        var: Scalar_String(
+            value: string
+        )
+        name: length
+        args: array(
+        )
+    )
+    3: Expr_FuncCall(
+        name: Expr_ArrayDimFetch(
+            var: Expr_PropertyFetch(
+                var: Expr_Clone(
+                    expr: Expr_Variable(
+                        name: obj
+                    )
+                )
+                name: b
+            )
+            dim: Scalar_LNumber(
+                value: 0
+            )
+        )
+        args: array(
+            0: Arg(
+                value: Scalar_LNumber(
+                    value: 1
+                )
+                byRef: false
+                unpack: false
+            )
+        )
+    )
+    4: Expr_Assign(
+        var: Expr_ArrayDimFetch(
+            var: Expr_Array(
+                items: array(
+                    0: Expr_ArrayItem(
+                        key: null
+                        value: Scalar_LNumber(
+                            value: 0
+                        )
+                        byRef: false
+                    )
+                    1: Expr_ArrayItem(
+                        key: null
+                        value: Scalar_LNumber(
+                            value: 1
+                        )
+                        byRef: false
+                    )
+                )
+            )
+            dim: Scalar_LNumber(
+                value: 0
+            )
+        )
+        expr: Scalar_LNumber(
+            value: 1
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/new.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/new.test
new file mode 100644
index 00000000000..e5f92f97a5b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/new.test
@@ -0,0 +1,95 @@
+UVS new expressions
+-----
+className;
+new Test::$className;
+new $test::$className;
+new $weird[0]->foo::$className;
+-----
+!!php7
+array(
+    0: Expr_New(
+        class: Expr_Variable(
+            name: className
+        )
+        args: array(
+        )
+    )
+    1: Expr_New(
+        class: Expr_ArrayDimFetch(
+            var: Expr_Variable(
+                name: array
+            )
+            dim: Scalar_String(
+                value: className
+            )
+        )
+        args: array(
+        )
+    )
+    2: Expr_New(
+        class: Expr_ArrayDimFetch(
+            var: Expr_Variable(
+                name: array
+            )
+            dim: Scalar_String(
+                value: className
+            )
+        )
+        args: array(
+        )
+    )
+    3: Expr_New(
+        class: Expr_PropertyFetch(
+            var: Expr_Variable(
+                name: obj
+            )
+            name: className
+        )
+        args: array(
+        )
+    )
+    4: Expr_New(
+        class: Expr_StaticPropertyFetch(
+            class: Name(
+                parts: array(
+                    0: Test
+                )
+            )
+            name: className
+        )
+        args: array(
+        )
+    )
+    5: Expr_New(
+        class: Expr_StaticPropertyFetch(
+            class: Expr_Variable(
+                name: test
+            )
+            name: className
+        )
+        args: array(
+        )
+    )
+    6: Expr_New(
+        class: Expr_StaticPropertyFetch(
+            class: Expr_PropertyFetch(
+                var: Expr_ArrayDimFetch(
+                    var: Expr_Variable(
+                        name: weird
+                    )
+                    dim: Scalar_LNumber(
+                        value: 0
+                    )
+                )
+                name: foo
+            )
+            name: className
+        )
+        args: array(
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/staticProperty.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/staticProperty.test
new file mode 100644
index 00000000000..5fadfc483e9
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/expr/uvs/staticProperty.test
@@ -0,0 +1,93 @@
+UVS static access
+-----
+c test
+EOS;
+
+// comment to force line break before EOF
+-----
+array(
+    0: Scalar_String(
+        value:
+    )
+    1: Scalar_String(
+        value:
+    )
+    2: Scalar_String(
+        value: Test '" $a \n
+    )
+    3: Scalar_String(
+        value: Test '" $a
+
+    )
+    4: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: Test
+            )
+            1: Expr_Variable(
+                name: a
+            )
+        )
+    )
+    5: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: Test
+            )
+            1: Expr_Variable(
+                name: a
+            )
+            2: Scalar_EncapsedStringPart(
+                value:  and
+            )
+            3: Expr_PropertyFetch(
+                var: Expr_Variable(
+                    name: b
+                )
+                name: c
+            )
+            4: Scalar_EncapsedStringPart(
+                value:  test
+            )
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/docStringNewlines.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/docStringNewlines.test
new file mode 100644
index 00000000000..bfd54fce91a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/docStringNewlines.test
@@ -0,0 +1,62 @@
+Trailing newlines in doc strings
+-----
+B";
+"$A[B]";
+"$A[0]";
+"$A[0x0]";
+"$A[$B]";
+"{$A}";
+"{$A['B']}";
+"${A}";
+"${A['B']}";
+"${$A}";
+"\{$A}";
+"\{ $A }";
+"\\{$A}";
+"\\{ $A }";
+"{$$A}[B]";
+"$$A[B]";
+"A $B C";
+b"$A";
+-----
+array(
+    0: Scalar_Encapsed(
+        parts: array(
+            0: Expr_Variable(
+                name: A
+            )
+        )
+    )
+    1: Scalar_Encapsed(
+        parts: array(
+            0: Expr_PropertyFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                name: B
+            )
+        )
+    )
+    2: Scalar_Encapsed(
+        parts: array(
+            0: Expr_ArrayDimFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                dim: Scalar_String(
+                    value: B
+                )
+            )
+        )
+    )
+    3: Scalar_Encapsed(
+        parts: array(
+            0: Expr_ArrayDimFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                dim: Scalar_String(
+                    value: 0
+                )
+            )
+        )
+    )
+    4: Scalar_Encapsed(
+        parts: array(
+            0: Expr_ArrayDimFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                dim: Scalar_String(
+                    value: 0x0
+                )
+            )
+        )
+    )
+    5: Scalar_Encapsed(
+        parts: array(
+            0: Expr_ArrayDimFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                dim: Expr_Variable(
+                    name: B
+                )
+            )
+        )
+    )
+    6: Scalar_Encapsed(
+        parts: array(
+            0: Expr_Variable(
+                name: A
+            )
+        )
+    )
+    7: Scalar_Encapsed(
+        parts: array(
+            0: Expr_ArrayDimFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                dim: Scalar_String(
+                    value: B
+                )
+            )
+        )
+    )
+    8: Scalar_Encapsed(
+        parts: array(
+            0: Expr_Variable(
+                name: A
+            )
+        )
+    )
+    9: Scalar_Encapsed(
+        parts: array(
+            0: Expr_ArrayDimFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                dim: Scalar_String(
+                    value: B
+                )
+            )
+        )
+    )
+    10: Scalar_Encapsed(
+        parts: array(
+            0: Expr_Variable(
+                name: Expr_Variable(
+                    name: A
+                )
+            )
+        )
+    )
+    11: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: \{
+            )
+            1: Expr_Variable(
+                name: A
+            )
+            2: Scalar_EncapsedStringPart(
+                value: }
+            )
+        )
+    )
+    12: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: \{
+            )
+            1: Expr_Variable(
+                name: A
+            )
+            2: Scalar_EncapsedStringPart(
+                value:  }
+            )
+        )
+    )
+    13: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: \
+            )
+            1: Expr_Variable(
+                name: A
+            )
+        )
+    )
+    14: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: \{
+            )
+            1: Expr_Variable(
+                name: A
+            )
+            2: Scalar_EncapsedStringPart(
+                value:  }
+            )
+        )
+    )
+    15: Scalar_Encapsed(
+        parts: array(
+            0: Expr_Variable(
+                name: Expr_Variable(
+                    name: A
+                )
+            )
+            1: Scalar_EncapsedStringPart(
+                value: [B]
+            )
+        )
+    )
+    16: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: $
+            )
+            1: Expr_ArrayDimFetch(
+                var: Expr_Variable(
+                    name: A
+                )
+                dim: Scalar_String(
+                    value: B
+                )
+            )
+        )
+    )
+    17: Scalar_Encapsed(
+        parts: array(
+            0: Scalar_EncapsedStringPart(
+                value: A
+            )
+            1: Expr_Variable(
+                name: B
+            )
+            2: Scalar_EncapsedStringPart(
+                value:  C
+            )
+        )
+    )
+    18: Scalar_Encapsed(
+        parts: array(
+            0: Expr_Variable(
+                name: A
+            )
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/float.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/float.test
new file mode 100644
index 00000000000..c91b7ac7b8d
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/float.test
@@ -0,0 +1,70 @@
+Different float syntaxes
+-----
+ float overflows
+// (all are actually the same number, just in different representations)
+18446744073709551615;
+0xFFFFFFFFFFFFFFFF;
+01777777777777777777777;
+0177777777777777777777787;
+0b1111111111111111111111111111111111111111111111111111111111111111;
+-----
+array(
+    0: Scalar_DNumber(
+        value: 0
+    )
+    1: Scalar_DNumber(
+        value: 0
+    )
+    2: Scalar_DNumber(
+        value: 0
+    )
+    3: Scalar_DNumber(
+        value: 0
+    )
+    4: Scalar_DNumber(
+        value: 0
+    )
+    5: Scalar_DNumber(
+        value: 0
+    )
+    6: Scalar_DNumber(
+        value: 0
+    )
+    7: Scalar_DNumber(
+        value: 302000000000
+    )
+    8: Scalar_DNumber(
+        value: 3.002E+102
+    )
+    9: Scalar_DNumber(
+        value: INF
+    )
+    10: Scalar_DNumber(
+        value: @@{ 0xFFFFFFFFFFFFFFFF }@@
+    )
+    11: Scalar_DNumber(
+        value: @@{ 0xFFFFFFFFFFFFFFFF }@@
+    )
+    12: Scalar_DNumber(
+        value: @@{ 0xFFFFFFFFFFFFFFFF }@@
+    )
+    13: Scalar_DNumber(
+        value: @@{ 0xFFFFFFFFFFFFFFFF }@@
+    )
+    14: Scalar_DNumber(
+        value: @@{ 0xFFFFFFFFFFFFFFFF }@@
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/int.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/int.test
new file mode 100644
index 00000000000..17a5785b623
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/scalar/int.test
@@ -0,0 +1,47 @@
+Different integer syntaxes
+-----
+array();
+$t->public();
+
+Test::list();
+Test::protected();
+
+$t->class;
+$t->private;
+
+Test::TRAIT;
+Test::FINAL;
+
+class Foo {
+    use TraitA, TraitB {
+        TraitA::catch insteadof namespace\TraitB;
+        TraitA::list as foreach;
+        TraitB::throw as protected public;
+        TraitB::self as protected;
+        exit as die;
+        \TraitC::exit as bye;
+        namespace\TraitC::exit as byebye;
+        TraitA::
+            //
+            /** doc comment */
+            #
+        catch /* comment */
+            // comment
+            # comment
+        insteadof TraitB;
+    }
+}
+-----
+array(
+    0: Stmt_Class(
+        type: 0
+        name: Test
+        extends: null
+        implements: array(
+        )
+        stmts: array(
+            0: Stmt_ClassMethod(
+                type: 0
+                byRef: false
+                name: array
+                params: array(
+                )
+                returnType: null
+                stmts: array(
+                )
+            )
+            1: Stmt_ClassMethod(
+                type: 0
+                byRef: false
+                name: public
+                params: array(
+                )
+                returnType: null
+                stmts: array(
+                )
+            )
+            2: Stmt_ClassMethod(
+                type: 8
+                byRef: false
+                name: list
+                params: array(
+                )
+                returnType: null
+                stmts: array(
+                )
+            )
+            3: Stmt_ClassMethod(
+                type: 8
+                byRef: false
+                name: protected
+                params: array(
+                )
+                returnType: null
+                stmts: array(
+                )
+            )
+            4: Stmt_Property(
+                type: 1
+                props: array(
+                    0: Stmt_PropertyProperty(
+                        name: class
+                        default: null
+                    )
+                )
+            )
+            5: Stmt_Property(
+                type: 1
+                props: array(
+                    0: Stmt_PropertyProperty(
+                        name: private
+                        default: null
+                    )
+                )
+            )
+            6: Stmt_ClassConst(
+                consts: array(
+                    0: Const(
+                        name: TRAIT
+                        value: Scalar_LNumber(
+                            value: 3
+                        )
+                    )
+                    1: Const(
+                        name: FINAL
+                        value: Scalar_LNumber(
+                            value: 4
+                        )
+                    )
+                )
+            )
+            7: Stmt_ClassConst(
+                consts: array(
+                    0: Const(
+                        name: __CLASS__
+                        value: Scalar_LNumber(
+                            value: 1
+                        )
+                    )
+                    1: Const(
+                        name: __TRAIT__
+                        value: Scalar_LNumber(
+                            value: 2
+                        )
+                    )
+                    2: Const(
+                        name: __FUNCTION__
+                        value: Scalar_LNumber(
+                            value: 3
+                        )
+                    )
+                    3: Const(
+                        name: __METHOD__
+                        value: Scalar_LNumber(
+                            value: 4
+                        )
+                    )
+                    4: Const(
+                        name: __LINE__
+                        value: Scalar_LNumber(
+                            value: 5
+                        )
+                    )
+                    5: Const(
+                        name: __FILE__
+                        value: Scalar_LNumber(
+                            value: 6
+                        )
+                    )
+                    6: Const(
+                        name: __DIR__
+                        value: Scalar_LNumber(
+                            value: 7
+                        )
+                    )
+                    7: Const(
+                        name: __NAMESPACE__
+                        value: Scalar_LNumber(
+                            value: 8
+                        )
+                    )
+                )
+            )
+        )
+    )
+    1: Expr_Assign(
+        var: Expr_Variable(
+            name: t
+        )
+        expr: Expr_New(
+            class: Name(
+                parts: array(
+                    0: Test
+                )
+            )
+            args: array(
+            )
+        )
+    )
+    2: Expr_MethodCall(
+        var: Expr_Variable(
+            name: t
+        )
+        name: array
+        args: array(
+        )
+    )
+    3: Expr_MethodCall(
+        var: Expr_Variable(
+            name: t
+        )
+        name: public
+        args: array(
+        )
+    )
+    4: Expr_StaticCall(
+        class: Name(
+            parts: array(
+                0: Test
+            )
+        )
+        name: list
+        args: array(
+        )
+    )
+    5: Expr_StaticCall(
+        class: Name(
+            parts: array(
+                0: Test
+            )
+        )
+        name: protected
+        args: array(
+        )
+    )
+    6: Expr_PropertyFetch(
+        var: Expr_Variable(
+            name: t
+        )
+        name: class
+    )
+    7: Expr_PropertyFetch(
+        var: Expr_Variable(
+            name: t
+        )
+        name: private
+    )
+    8: Expr_ClassConstFetch(
+        class: Name(
+            parts: array(
+                0: Test
+            )
+        )
+        name: TRAIT
+    )
+    9: Expr_ClassConstFetch(
+        class: Name(
+            parts: array(
+                0: Test
+            )
+        )
+        name: FINAL
+    )
+    10: Stmt_Class(
+        type: 0
+        name: Foo
+        extends: null
+        implements: array(
+        )
+        stmts: array(
+            0: Stmt_TraitUse(
+                traits: array(
+                    0: Name(
+                        parts: array(
+                            0: TraitA
+                        )
+                    )
+                    1: Name(
+                        parts: array(
+                            0: TraitB
+                        )
+                    )
+                )
+                adaptations: array(
+                    0: Stmt_TraitUseAdaptation_Precedence(
+                        trait: Name(
+                            parts: array(
+                                0: TraitA
+                            )
+                        )
+                        method: catch
+                        insteadof: array(
+                            0: Name_Relative(
+                                parts: array(
+                                    0: TraitB
+                                )
+                            )
+                        )
+                    )
+                    1: Stmt_TraitUseAdaptation_Alias(
+                        trait: Name(
+                            parts: array(
+                                0: TraitA
+                            )
+                        )
+                        method: list
+                        newModifier: null
+                        newName: foreach
+                    )
+                    2: Stmt_TraitUseAdaptation_Alias(
+                        trait: Name(
+                            parts: array(
+                                0: TraitB
+                            )
+                        )
+                        method: throw
+                        newModifier: 2
+                        newName: public
+                    )
+                    3: Stmt_TraitUseAdaptation_Alias(
+                        trait: Name(
+                            parts: array(
+                                0: TraitB
+                            )
+                        )
+                        method: self
+                        newModifier: 2
+                        newName: null
+                    )
+                    4: Stmt_TraitUseAdaptation_Alias(
+                        trait: null
+                        method: exit
+                        newModifier: null
+                        newName: die
+                    )
+                    5: Stmt_TraitUseAdaptation_Alias(
+                        trait: Name_FullyQualified(
+                            parts: array(
+                                0: TraitC
+                            )
+                        )
+                        method: exit
+                        newModifier: null
+                        newName: bye
+                    )
+                    6: Stmt_TraitUseAdaptation_Alias(
+                        trait: Name_Relative(
+                            parts: array(
+                                0: TraitC
+                            )
+                        )
+                        method: exit
+                        newModifier: null
+                        newName: byebye
+                    )
+                    7: Stmt_TraitUseAdaptation_Precedence(
+                        trait: Name(
+                            parts: array(
+                                0: TraitA
+                            )
+                        )
+                        method: catch
+                        insteadof: array(
+                            0: Name(
+                                parts: array(
+                                    0: TraitB
+                                )
+                            )
+                        )
+                    )
+                )
+            )
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/blocklessStatement.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/blocklessStatement.test
new file mode 100644
index 00000000000..ae83dabb990
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/blocklessStatement.test
@@ -0,0 +1,112 @@
+Blockless statements for if/for/etc
+-----
+ 'baz']
+) {}
+-----
+array(
+    0: Stmt_Function(
+        byRef: false
+        name: a
+        params: array(
+            0: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: b
+                default: Expr_ConstFetch(
+                    name: Name(
+                        parts: array(
+                            0: null
+                        )
+                    )
+                )
+            )
+            1: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: c
+                default: Scalar_String(
+                    value: foo
+                )
+            )
+            2: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: d
+                default: Expr_ClassConstFetch(
+                    class: Name(
+                        parts: array(
+                            0: A
+                        )
+                    )
+                    name: B
+                )
+            )
+            3: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: f
+                default: Expr_UnaryPlus(
+                    expr: Scalar_LNumber(
+                        value: 1
+                    )
+                )
+            )
+            4: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: g
+                default: Expr_UnaryMinus(
+                    expr: Scalar_DNumber(
+                        value: 1
+                    )
+                )
+            )
+            5: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: h
+                default: Expr_Array(
+                    items: array(
+                    )
+                )
+            )
+            6: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: i
+                default: Expr_Array(
+                    items: array(
+                    )
+                )
+            )
+            7: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: j
+                default: Expr_Array(
+                    items: array(
+                        0: Expr_ArrayItem(
+                            key: null
+                            value: Scalar_String(
+                                value: foo
+                            )
+                            byRef: false
+                        )
+                    )
+                )
+            )
+            8: Param(
+                type: null
+                byRef: false
+                variadic: false
+                name: k
+                default: Expr_Array(
+                    items: array(
+                        0: Expr_ArrayItem(
+                            key: null
+                            value: Scalar_String(
+                                value: foo
+                            )
+                            byRef: false
+                        )
+                        1: Expr_ArrayItem(
+                            key: Scalar_String(
+                                value: bar
+                            )
+                            value: Scalar_String(
+                                value: baz
+                            )
+                            byRef: false
+                        )
+                    )
+                )
+            )
+        )
+        returnType: null
+        stmts: array(
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/function/returnTypes.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/function/returnTypes.test
new file mode 100644
index 00000000000..ca6c3106e4f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/function/returnTypes.test
@@ -0,0 +1,52 @@
+Return type declarations
+-----
+ $value;
+
+    // expressions
+    $data = yield;
+    $data = (yield $value);
+    $data = (yield $key => $value);
+
+    // yield in language constructs with their own parentheses
+    if (yield $foo); elseif (yield $foo);
+    if (yield $foo): elseif (yield $foo): endif;
+    while (yield $foo);
+    do {} while (yield $foo);
+    switch (yield $foo) {}
+    die(yield $foo);
+
+    // yield in function calls
+    func(yield $foo);
+    $foo->func(yield $foo);
+    new Foo(yield $foo);
+
+    yield from $foo;
+    yield from $foo and yield from $bar;
+    yield from $foo + $bar;
+}
+-----
+array(
+    0: Stmt_Function(
+        byRef: false
+        name: gen
+        params: array(
+        )
+        returnType: null
+        stmts: array(
+            0: Expr_Yield(
+                key: null
+                value: null
+            )
+            1: Expr_Yield(
+                key: null
+                value: Expr_Variable(
+                    name: value
+                )
+            )
+            2: Expr_Yield(
+                key: Expr_Variable(
+                    name: key
+                )
+                value: Expr_Variable(
+                    name: value
+                )
+            )
+            3: Expr_Assign(
+                var: Expr_Variable(
+                    name: data
+                )
+                expr: Expr_Yield(
+                    key: null
+                    value: null
+                )
+            )
+            4: Expr_Assign(
+                var: Expr_Variable(
+                    name: data
+                )
+                expr: Expr_Yield(
+                    key: null
+                    value: Expr_Variable(
+                        name: value
+                    )
+                )
+            )
+            5: Expr_Assign(
+                var: Expr_Variable(
+                    name: data
+                )
+                expr: Expr_Yield(
+                    key: Expr_Variable(
+                        name: key
+                    )
+                    value: Expr_Variable(
+                        name: value
+                    )
+                )
+            )
+            6: Stmt_If(
+                cond: Expr_Yield(
+                    key: null
+                    value: Expr_Variable(
+                        name: foo
+                    )
+                )
+                stmts: array(
+                )
+                elseifs: array(
+                    0: Stmt_ElseIf(
+                        cond: Expr_Yield(
+                            key: null
+                            value: Expr_Variable(
+                                name: foo
+                            )
+                        )
+                        stmts: array(
+                        )
+                    )
+                )
+                else: null
+            )
+            7: Stmt_If(
+                cond: Expr_Yield(
+                    key: null
+                    value: Expr_Variable(
+                        name: foo
+                    )
+                )
+                stmts: array(
+                )
+                elseifs: array(
+                    0: Stmt_ElseIf(
+                        cond: Expr_Yield(
+                            key: null
+                            value: Expr_Variable(
+                                name: foo
+                            )
+                        )
+                        stmts: array(
+                        )
+                    )
+                )
+                else: null
+            )
+            8: Stmt_While(
+                cond: Expr_Yield(
+                    key: null
+                    value: Expr_Variable(
+                        name: foo
+                    )
+                )
+                stmts: array(
+                )
+            )
+            9: Stmt_Do(
+                cond: Expr_Yield(
+                    key: null
+                    value: Expr_Variable(
+                        name: foo
+                    )
+                )
+                stmts: array(
+                )
+            )
+            10: Stmt_Switch(
+                cond: Expr_Yield(
+                    key: null
+                    value: Expr_Variable(
+                        name: foo
+                    )
+                )
+                cases: array(
+                )
+            )
+            11: Expr_Exit(
+                expr: Expr_Yield(
+                    key: null
+                    value: Expr_Variable(
+                        name: foo
+                    )
+                )
+            )
+            12: Expr_FuncCall(
+                name: Name(
+                    parts: array(
+                        0: func
+                    )
+                )
+                args: array(
+                    0: Arg(
+                        value: Expr_Yield(
+                            key: null
+                            value: Expr_Variable(
+                                name: foo
+                            )
+                        )
+                        byRef: false
+                        unpack: false
+                    )
+                )
+            )
+            13: Expr_MethodCall(
+                var: Expr_Variable(
+                    name: foo
+                )
+                name: func
+                args: array(
+                    0: Arg(
+                        value: Expr_Yield(
+                            key: null
+                            value: Expr_Variable(
+                                name: foo
+                            )
+                        )
+                        byRef: false
+                        unpack: false
+                    )
+                )
+            )
+            14: Expr_New(
+                class: Name(
+                    parts: array(
+                        0: Foo
+                    )
+                )
+                args: array(
+                    0: Arg(
+                        value: Expr_Yield(
+                            key: null
+                            value: Expr_Variable(
+                                name: foo
+                            )
+                        )
+                        byRef: false
+                        unpack: false
+                    )
+                )
+            )
+            15: Expr_YieldFrom(
+                expr: Expr_Variable(
+                    name: foo
+                )
+            )
+            16: Expr_BinaryOp_LogicalAnd(
+                left: Expr_YieldFrom(
+                    expr: Expr_Variable(
+                        name: foo
+                    )
+                )
+                right: Expr_YieldFrom(
+                    expr: Expr_Variable(
+                        name: bar
+                    )
+                )
+            )
+            17: Expr_YieldFrom(
+                expr: Expr_BinaryOp_Plus(
+                    left: Expr_Variable(
+                        name: foo
+                    )
+                    right: Expr_Variable(
+                        name: bar
+                    )
+                )
+            )
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/generator/yieldPrecedence.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/generator/yieldPrecedence.test
new file mode 100644
index 00000000000..ff0d4df0843
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/generator/yieldPrecedence.test
@@ -0,0 +1,230 @@
+Yield operator precedence
+-----
+ "a" . "b";
+    yield "k" => "a" or die;
+    var_dump([yield "k" => "a" . "b"]);
+    yield yield "k1" => yield "k2" => "a" . "b";
+    yield yield "k1" => (yield "k2") => "a" . "b";
+    var_dump([yield "k1" => yield "k2" => "a" . "b"]);
+    var_dump([yield "k1" => (yield "k2") => "a" . "b"]);
+}
+-----
+!!php7
+array(
+    0: Stmt_Function(
+        byRef: false
+        name: gen
+        params: array(
+        )
+        returnType: null
+        stmts: array(
+            0: Expr_Yield(
+                key: null
+                value: Expr_BinaryOp_Concat(
+                    left: Scalar_String(
+                        value: a
+                    )
+                    right: Scalar_String(
+                        value: b
+                    )
+                )
+            )
+            1: Expr_BinaryOp_LogicalOr(
+                left: Expr_Yield(
+                    key: null
+                    value: Scalar_String(
+                        value: a
+                    )
+                )
+                right: Expr_Exit(
+                    expr: null
+                )
+            )
+            2: Expr_Yield(
+                key: Scalar_String(
+                    value: k
+                )
+                value: Expr_BinaryOp_Concat(
+                    left: Scalar_String(
+                        value: a
+                    )
+                    right: Scalar_String(
+                        value: b
+                    )
+                )
+            )
+            3: Expr_BinaryOp_LogicalOr(
+                left: Expr_Yield(
+                    key: Scalar_String(
+                        value: k
+                    )
+                    value: Scalar_String(
+                        value: a
+                    )
+                )
+                right: Expr_Exit(
+                    expr: null
+                )
+            )
+            4: Expr_FuncCall(
+                name: Name(
+                    parts: array(
+                        0: var_dump
+                    )
+                )
+                args: array(
+                    0: Arg(
+                        value: Expr_Array(
+                            items: array(
+                                0: Expr_ArrayItem(
+                                    key: null
+                                    value: Expr_Yield(
+                                        key: Scalar_String(
+                                            value: k
+                                        )
+                                        value: Expr_BinaryOp_Concat(
+                                            left: Scalar_String(
+                                                value: a
+                                            )
+                                            right: Scalar_String(
+                                                value: b
+                                            )
+                                        )
+                                    )
+                                    byRef: false
+                                )
+                            )
+                        )
+                        byRef: false
+                        unpack: false
+                    )
+                )
+            )
+            5: Expr_Yield(
+                key: null
+                value: Expr_Yield(
+                    key: Scalar_String(
+                        value: k1
+                    )
+                    value: Expr_Yield(
+                        key: Scalar_String(
+                            value: k2
+                        )
+                        value: Expr_BinaryOp_Concat(
+                            left: Scalar_String(
+                                value: a
+                            )
+                            right: Scalar_String(
+                                value: b
+                            )
+                        )
+                    )
+                )
+            )
+            6: Expr_Yield(
+                key: Expr_Yield(
+                    key: Scalar_String(
+                        value: k1
+                    )
+                    value: Expr_Yield(
+                        key: null
+                        value: Scalar_String(
+                            value: k2
+                        )
+                    )
+                )
+                value: Expr_BinaryOp_Concat(
+                    left: Scalar_String(
+                        value: a
+                    )
+                    right: Scalar_String(
+                        value: b
+                    )
+                )
+            )
+            7: Expr_FuncCall(
+                name: Name(
+                    parts: array(
+                        0: var_dump
+                    )
+                )
+                args: array(
+                    0: Arg(
+                        value: Expr_Array(
+                            items: array(
+                                0: Expr_ArrayItem(
+                                    key: null
+                                    value: Expr_Yield(
+                                        key: Scalar_String(
+                                            value: k1
+                                        )
+                                        value: Expr_Yield(
+                                            key: Scalar_String(
+                                                value: k2
+                                            )
+                                            value: Expr_BinaryOp_Concat(
+                                                left: Scalar_String(
+                                                    value: a
+                                                )
+                                                right: Scalar_String(
+                                                    value: b
+                                                )
+                                            )
+                                        )
+                                    )
+                                    byRef: false
+                                )
+                            )
+                        )
+                        byRef: false
+                        unpack: false
+                    )
+                )
+            )
+            8: Expr_FuncCall(
+                name: Name(
+                    parts: array(
+                        0: var_dump
+                    )
+                )
+                args: array(
+                    0: Arg(
+                        value: Expr_Array(
+                            items: array(
+                                0: Expr_ArrayItem(
+                                    key: Expr_Yield(
+                                        key: Scalar_String(
+                                            value: k1
+                                        )
+                                        value: Expr_Yield(
+                                            key: null
+                                            value: Scalar_String(
+                                                value: k2
+                                            )
+                                        )
+                                    )
+                                    value: Expr_BinaryOp_Concat(
+                                        left: Scalar_String(
+                                            value: a
+                                        )
+                                        right: Scalar_String(
+                                            value: b
+                                        )
+                                    )
+                                    byRef: false
+                                )
+                            )
+                        )
+                        byRef: false
+                        unpack: false
+                    )
+                )
+            )
+        )
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/generator/yieldUnaryPrecedence.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/generator/yieldUnaryPrecedence.test
new file mode 100644
index 00000000000..13f96602cb1
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/generator/yieldUnaryPrecedence.test
@@ -0,0 +1,48 @@
+Yield with unary operator argument
+-----
+
+Hallo World!
+-----
+array(
+    0: Expr_Variable(
+        name: a
+    )
+    1: Stmt_HaltCompiler(
+        remaining: Hallo World!
+    )
+)
+-----
+
+#!/usr/bin/env php
+-----
+array(
+    0: Stmt_InlineHTML(
+        value: #!/usr/bin/env php
+
+    )
+    1: Stmt_Echo(
+        exprs: array(
+            0: Scalar_String(
+                value: foobar
+            )
+        )
+    )
+    2: Stmt_InlineHTML(
+        value: #!/usr/bin/env php
+    )
+)
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/if.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/if.test
new file mode 100644
index 00000000000..6ae1e1652c3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/if.test
@@ -0,0 +1,95 @@
+If/Elseif/Else
+-----
+
+B
+
+ $c) {}
+foreach ($a as $b => &$c) {}
+foreach ($a as list($a, $b)) {}
+foreach ($a as $a => list($b, , $c)) {}
+
+// foreach on expression
+foreach (array() as $b) {}
+
+// alternative syntax
+foreach ($a as $b):
+endforeach;
+-----
+array(
+    0: Stmt_Foreach(
+        expr: Expr_Variable(
+            name: a
+        )
+        keyVar: null
+        byRef: false
+        valueVar: Expr_Variable(
+            name: b
+        )
+        stmts: array(
+        )
+    )
+    1: Stmt_Foreach(
+        expr: Expr_Variable(
+            name: a
+        )
+        keyVar: null
+        byRef: true
+        valueVar: Expr_Variable(
+            name: b
+        )
+        stmts: array(
+        )
+    )
+    2: Stmt_Foreach(
+        expr: Expr_Variable(
+            name: a
+        )
+        keyVar: Expr_Variable(
+            name: b
+        )
+        byRef: false
+        valueVar: Expr_Variable(
+            name: c
+        )
+        stmts: array(
+        )
+    )
+    3: Stmt_Foreach(
+        expr: Expr_Variable(
+            name: a
+        )
+        keyVar: Expr_Variable(
+            name: b
+        )
+        byRef: true
+        valueVar: Expr_Variable(
+            name: c
+        )
+        stmts: array(
+        )
+    )
+    4: Stmt_Foreach(
+        expr: Expr_Variable(
+            name: a
+        )
+        keyVar: null
+        byRef: false
+        valueVar: Expr_List(
+            vars: array(
+                0: Expr_Variable(
+                    name: a
+                )
+                1: Expr_Variable(
+                    name: b
+                )
+            )
+        )
+        stmts: array(
+        )
+    )
+    5: Stmt_Foreach(
+        expr: Expr_Variable(
+            name: a
+        )
+        keyVar: Expr_Variable(
+            name: a
+        )
+        byRef: false
+        valueVar: Expr_List(
+            vars: array(
+                0: Expr_Variable(
+                    name: b
+                )
+                1: null
+                2: Expr_Variable(
+                    name: c
+                )
+            )
+        )
+        stmts: array(
+        )
+    )
+    6: Stmt_Foreach(
+        expr: Expr_Array(
+            items: array(
+            )
+        )
+        keyVar: null
+        byRef: false
+        valueVar: Expr_Variable(
+            name: b
+        )
+        stmts: array(
+        )
+    )
+    7: Stmt_Foreach(
+        expr: Expr_Variable(
+            name: a
+        )
+        keyVar: null
+        byRef: false
+        valueVar: Expr_Variable(
+            name: b
+        )
+        stmts: array(
+        )
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/loop/while.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/loop/while.test
new file mode 100644
index 00000000000..65f6b2336f5
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/loop/while.test
@@ -0,0 +1,25 @@
+While loop
+-----
+
+Hi!
+-----
+array(
+    0: Stmt_Declare(
+        declares: array(
+            0: Stmt_DeclareDeclare(
+                key: A
+                value: Scalar_String(
+                    value: B
+                )
+            )
+        )
+        stmts: null
+    )
+    1: Stmt_Namespace(
+        name: Name(
+            parts: array(
+                0: B
+            )
+        )
+        stmts: array(
+        )
+    )
+    2: Stmt_HaltCompiler(
+        remaining: Hi!
+    )
+)
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/namespace/outsideStmtInvalid.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/namespace/outsideStmtInvalid.test
new file mode 100644
index 00000000000..52e884329da
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/parser/stmt/namespace/outsideStmtInvalid.test
@@ -0,0 +1,20 @@
+There (mostly) can't be statements outside of namespaces
+-----
+a = $a;
+    }
+};
+-----
+new class
+{
+};
+new class extends A implements B, C
+{
+};
+new class($a) extends A
+{
+    private $a;
+    public function __construct($a)
+    {
+        $this->a = $a;
+    }
+};
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/call.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/call.test
new file mode 100644
index 00000000000..0ec8925cf29
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/call.test
@@ -0,0 +1,13 @@
+Calls
+-----
+> $b;
+$a < $b;
+$a <= $b;
+$a > $b;
+$a >= $b;
+$a == $b;
+$a != $b;
+$a <> $b;
+$a === $b;
+$a !== $b;
+$a <=> $b;
+$a & $b;
+$a ^ $b;
+$a | $b;
+$a && $b;
+$a || $b;
+$a ? $b : $c;
+$a ?: $c;
+$a ?? $c;
+
+$a = $b;
+$a **= $b;
+$a *= $b;
+$a /= $b;
+$a %= $b;
+$a += $b;
+$a -= $b;
+$a .= $b;
+$a <<= $b;
+$a >>= $b;
+$a &= $b;
+$a ^= $b;
+$a |= $b;
+$a =& $b;
+
+$a and $b;
+$a xor $b;
+$a or $b;
+
+$a instanceof Foo;
+$a instanceof $b;
+-----
+$a ** $b;
+++$a;
+--$a;
+$a++;
+$a--;
+@$a;
+~$a;
+-$a;
++$a;
+(int) $a;
+(int) $a;
+(double) $a;
+(double) $a;
+(double) $a;
+(string) $a;
+(string) $a;
+(array) $a;
+(object) $a;
+(bool) $a;
+(bool) $a;
+(unset) $a;
+$a * $b;
+$a / $b;
+$a % $b;
+$a + $b;
+$a - $b;
+$a . $b;
+$a << $b;
+$a >> $b;
+$a < $b;
+$a <= $b;
+$a > $b;
+$a >= $b;
+$a == $b;
+$a != $b;
+$a != $b;
+$a === $b;
+$a !== $b;
+$a <=> $b;
+$a & $b;
+$a ^ $b;
+$a | $b;
+$a && $b;
+$a || $b;
+$a ? $b : $c;
+$a ?: $c;
+$a ?? $c;
+$a = $b;
+$a **= $b;
+$a *= $b;
+$a /= $b;
+$a %= $b;
+$a += $b;
+$a -= $b;
+$a .= $b;
+$a <<= $b;
+$a >>= $b;
+$a &= $b;
+$a ^= $b;
+$a |= $b;
+$a =& $b;
+$a and $b;
+$a xor $b;
+$a or $b;
+$a instanceof Foo;
+$a instanceof $b;
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/parentheses.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/parentheses.test
new file mode 100644
index 00000000000..1f18b659009
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/parentheses.test
@@ -0,0 +1,77 @@
+Pretty printer generates least-parentheses output
+-----
+ 0) > (1 < 0);
+++$a + $b;
+$a + $b++;
+
+$a ** $b ** $c;
+($a ** $b) ** $c;
+-1 ** 2;
+
+yield from $a and yield from $b;
+yield from ($a and yield from $b);
+
+print ($a and print $b);
+
+// The following will currently add unnecessary parentheses, because the pretty printer is not aware that assignment
+// and incdec only work on variables.
+!$a = $b;
+++$a ** $b;
+$a ** $b++;
+-----
+echo 'abc' . 'cde' . 'fgh';
+echo 'abc' . ('cde' . 'fgh');
+echo 'abc' . 1 + 2 . 'fgh';
+echo 'abc' . (1 + 2) . 'fgh';
+echo 1 * 2 + 3 / 4 % 5 . 6;
+echo 1 * (2 + 3) / (4 % (5 . 6));
+$a = $b = $c = $d = $f && true;
+($a = $b = $c = $d = $f) && true;
+$a = $b = $c = $d = $f and true;
+$a = $b = $c = $d = ($f and true);
+$a ? $b : $c ? $d : $e ? $f : $g;
+$a ? $b : ($c ? $d : ($e ? $f : $g));
+$a ? $b ? $c : $d : $f;
+$a ?? $b ?? $c;
+($a ?? $b) ?? $c;
+$a ?? ($b ? $c : $d);
+$a || ($b ?? $c);
+(1 > 0) > (1 < 0);
+++$a + $b;
+$a + $b++;
+$a ** $b ** $c;
+($a ** $b) ** $c;
+-1 ** 2;
+yield from $a and yield from $b;
+yield from ($a and yield from $b);
+print ($a and print $b);
+// The following will currently add unnecessary parentheses, because the pretty printer is not aware that assignment
+// and incdec only work on variables.
+!($a = $b);
+(++$a) ** $b;
+$a ** ($b++);
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/shortArraySyntax.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/shortArraySyntax.test
new file mode 100644
index 00000000000..227adb64d48
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/shortArraySyntax.test
@@ -0,0 +1,12 @@
+Short array syntax
+-----
+ 'b', 'c' => 'd'];
+-----
+!!both {"shortArraySyntax": true}
+[];
+[1, 2, 3];
+['a' => 'b', 'c' => 'd'];
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/uvs.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/uvs.test
new file mode 100644
index 00000000000..e336fb578ac
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/uvs.test
@@ -0,0 +1,23 @@
+Uniform variable syntax
+-----
+b)();
+(A::$b)();
+-----
+!!php7
+(function () {
+})();
+array('a', 'b')()();
+A::$b::$c;
+$A::$b[$c]();
+$A::{$b[$c]}();
+A::${$b}[$c]();
+($a->b)();
+(A::$b)();
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/variables.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/variables.test
new file mode 100644
index 00000000000..4e0fa2e123a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/variables.test
@@ -0,0 +1,73 @@
+Variables
+-----
+b;
+$a->b();
+$a->b($c);
+$a->$b();
+$a->{$b}();
+$a->$b[$c]();
+$$a->b;
+$a[$b];
+$a[$b]();
+$$a[$b];
+$a::B;
+$a::$b;
+$a::b();
+$a::b($c);
+$a::$b();
+$a::$b[$c];
+$a::$b[$c]($d);
+$a::{$b[$c]}($d);
+$a::{$b->c}();
+A::$$b[$c]();
+a();
+$a();
+$a()[$b];
+$a->b()[$c];
+$a::$b()[$c];
+(new A)->b;
+(new A())->b();
+(new $$a)[$b];
+(new $a->b)->c;
+
+global $a, $$a, $$a[$b], $$a->b;
+-----
+!!php5
+$a;
+${$a};
+${$a};
+$a->b;
+$a->b();
+$a->b($c);
+$a->{$b}();
+$a->{$b}();
+$a->{$b[$c]}();
+${$a}->b;
+$a[$b];
+$a[$b]();
+${$a[$b]};
+$a::B;
+$a::$b;
+$a::b();
+$a::b($c);
+$a::$b();
+$a::$b[$c];
+$a::{$b[$c]}($d);
+$a::{$b[$c]}($d);
+$a::{$b->c}();
+A::${$b[$c]}();
+a();
+$a();
+$a()[$b];
+$a->b()[$c];
+$a::$b()[$c];
+(new A())->b;
+(new A())->b();
+(new ${$a}())[$b];
+(new $a->b())->c;
+global $a, ${$a}, ${$a[$b]}, ${$a->b};
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/yield.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/yield.test
new file mode 100644
index 00000000000..ba03badde55
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/expr/yield.test
@@ -0,0 +1,44 @@
+Yield
+-----
+ $b;
+    $a = yield;
+    $a = (yield $b);
+    $a = (yield $b => $c);
+}
+// TODO Get rid of parens for cases 2 and 3
+-----
+function gen()
+{
+    yield;
+    (yield $a);
+    (yield $a => $b);
+    $a = yield;
+    $a = (yield $b);
+    $a = (yield $b => $c);
+}
+-----
+ $c;
+    yield from $a;
+    $a = yield from $b;
+}
+// TODO Get rid of parens for last case
+-----
+!!php7
+function gen()
+{
+    $a = (yield $b);
+    $a = (yield $b => $c);
+    yield from $a;
+    $a = (yield from $b);
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/inlineHTMLandPHPtest.file-test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/inlineHTMLandPHPtest.file-test
new file mode 100644
index 00000000000..74a26850597
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/inlineHTMLandPHPtest.file-test
@@ -0,0 +1,52 @@
+File containing both inline HTML and PHP
+-----
+HTML
+
+HTML
+-----
+
+HTML
+-----
+HTML
+
+HTML
+-----
+HTML
+
+HTML
+-----
+HTML
+
+HTML
+
+HTML
+-----
+HTML
+
+HTML
+
+HTML
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/onlyInlineHTML.file-test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/onlyInlineHTML.file-test
new file mode 100644
index 00000000000..0e2d4f7756f
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/onlyInlineHTML.file-test
@@ -0,0 +1,11 @@
+File containing only inline HTML
+-----
+Hallo World
+Foo Bar
+Bar Foo
+World Hallo
+-----
+Hallo World
+Foo Bar
+Bar Foo
+World Hallo
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/onlyPHP.file-test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/onlyPHP.file-test
new file mode 100644
index 00000000000..32d1862f8b9
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/onlyPHP.file-test
@@ -0,0 +1,16 @@
+File containing only PHP
+-----
+a = 'bar';
+        echo 'test';
+    }
+
+    protected function baz() {}
+    public function foo() {}
+    abstract static function bar() {}
+}
+
+trait Bar
+{
+    function test()
+    {
+    }
+}
+-----
+class Foo extends Bar implements ABC, \DEF, namespace\GHI
+{
+    var $a = 'foo';
+    private $b = 'bar';
+    static $c = 'baz';
+    function test()
+    {
+        $this->a = 'bar';
+        echo 'test';
+    }
+    protected function baz()
+    {
+    }
+    public function foo()
+    {
+    }
+    static abstract function bar()
+    {
+    }
+}
+trait Bar
+{
+    function test()
+    {
+    }
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/stmt/const.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/stmt/const.test
new file mode 100644
index 00000000000..6b1764206b1
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/stmt/const.test
@@ -0,0 +1,11 @@
+Constant declarations
+-----
+ $val) {
+
+}
+
+foreach ($arr as $key => &$val) {
+
+}
+-----
+foreach ($arr as $val) {
+}
+foreach ($arr as &$val) {
+}
+foreach ($arr as $key => $val) {
+}
+foreach ($arr as $key => &$val) {
+}
\ No newline at end of file
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/stmt/function_signatures.test b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/stmt/function_signatures.test
new file mode 100644
index 00000000000..af1088a051a
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/patcher/third_party/PHP-Parser/test/code/prettyPrinter/stmt/function_signatures.test
@@ -0,0 +1,43 @@
+Function signatures
+-----
+ 0) {
+    if (count($options) === 1 && $options[0] === '--no-progress') {
+        $showProgress = false;
+    } else {
+        showHelp('Invalid option passed!');
+    }
+}
+
+$testType = $arguments[0];
+$dir = $arguments[1];
+
+switch ($testType) {
+    case 'Symfony':
+        $version = 'Php5';
+        $fileFilter = function($path) {
+            return preg_match('~\.php(?:\.cache)?$~', $path) && false === strpos($path, 'skeleton');
+        };
+        $codeExtractor = function($file, $code) {
+            return $code;
+        };
+        break;
+    case 'PHP5':
+    case 'PHP7':
+    $version = $testType === 'PHP5' ? 'Php5' : 'Php7';
+        $fileFilter = function($path) {
+            return preg_match('~\.phpt$~', $path);
+        };
+        $codeExtractor = function($file, $code) {
+            if (preg_match('~(?:
+# skeleton files
+  ext.gmp.tests.001
+| ext.skeleton.tests.001
+# multibyte encoded files
+| ext.mbstring.tests.zend_multibyte-01
+| Zend.tests.multibyte.multibyte_encoding_001
+| Zend.tests.multibyte.multibyte_encoding_004
+| Zend.tests.multibyte.multibyte_encoding_005
+# token_get_all bug (https://bugs.php.net/bug.php?id=60097)
+| Zend.tests.bug47516
+# pretty print difference due to INF vs 1e1000
+| ext.standard.tests.general_functions.bug27678
+| tests.lang.bug24640
+)\.phpt$~x', $file)) {
+                return null;
+            }
+
+            if (!preg_match('~--FILE--\s*(.*?)--[A-Z]+--~s', $code, $matches)) {
+                return null;
+            }
+            if (preg_match('~--EXPECT(?:F|REGEX)?--\s*(?:Parse|Fatal) error~', $code)) {
+                return null;
+            }
+
+            return $matches[1];
+        };
+        break;
+    default:
+        showHelp('Test type must be one of: PHP5, PHP7 or Symfony');
+}
+
+require_once dirname(__FILE__) . '/../lib/PhpParser/Autoloader.php';
+PhpParser\Autoloader::register();
+
+$parserName    = 'PhpParser\Parser\\' . $version;
+$parser        = new $parserName(new PhpParser\Lexer\Emulative);
+$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
+$nodeDumper    = new PhpParser\NodeDumper;
+
+$parseFail = $ppFail = $compareFail = $count = 0;
+
+$readTime = $parseTime = $ppTime = $reparseTime = $compareTime = 0;
+$totalStartTime = microtime(true);
+
+foreach (new RecursiveIteratorIterator(
+             new RecursiveDirectoryIterator($dir),
+             RecursiveIteratorIterator::LEAVES_ONLY)
+         as $file) {
+    if (!$fileFilter($file)) {
+        continue;
+    }
+
+    $startTime = microtime(true);
+    $code = file_get_contents($file);
+    $readTime += microtime(true) - $startTime;
+
+    if (null === $code = $codeExtractor($file, $code)) {
+        continue;
+    }
+
+    set_time_limit(10);
+
+    ++$count;
+
+    if ($showProgress) {
+        echo substr(str_pad('Testing file ' . $count . ': ' . substr($file, strlen($dir)), 79), 0, 79), "\r";
+    }
+
+    try {
+        $startTime = microtime(true);
+        $stmts = $parser->parse($code);
+        $parseTime += microtime(true) - $startTime;
+
+        $startTime = microtime(true);
+        $code = 'prettyPrint($stmts);
+        $ppTime += microtime(true) - $startTime;
+
+        try {
+            $startTime = microtime(true);
+            $ppStmts = $parser->parse($code);
+            $reparseTime += microtime(true) - $startTime;
+
+            $startTime = microtime(true);
+            $same = $nodeDumper->dump($stmts) == $nodeDumper->dump($ppStmts);
+            $compareTime += microtime(true) - $startTime;
+
+            if (!$same) {
+                echo $file, ":\n    Result of initial parse and parse after pretty print differ\n";
+
+                ++$compareFail;
+            }
+        } catch (PhpParser\Error $e) {
+            echo $file, ":\n    Parse of pretty print failed with message: {$e->getMessage()}\n";
+
+            ++$ppFail;
+        }
+    } catch (PhpParser\Error $e) {
+        echo $file, ":\n    Parse failed with message: {$e->getMessage()}\n";
+
+        ++$parseFail;
+    }
+}
+
+if (0 === $parseFail && 0 === $ppFail && 0 === $compareFail) {
+    echo "\n\n", 'All tests passed.', "\n";
+} else {
+    echo "\n\n", '==========', "\n\n", 'There were: ', "\n";
+    if (0 !== $parseFail) {
+        echo '    ', $parseFail,   ' parse failures.',        "\n";
+    }
+    if (0 !== $ppFail) {
+        echo '    ', $ppFail,      ' pretty print failures.', "\n";
+    }
+    if (0 !== $compareFail) {
+        echo '    ', $compareFail, ' compare failures.',      "\n";
+    }
+}
+
+echo "\n",
+     'Tested files:         ', $count,        "\n",
+     "\n",
+     'Reading files took:   ', $readTime,    "\n",
+     'Parsing took:         ', $parseTime,   "\n",
+     'Pretty printing took: ', $ppTime,      "\n",
+     'Reparsing took:       ', $reparseTime, "\n",
+     'Comparing took:       ', $compareTime, "\n",
+     "\n",
+     'Total time:           ', microtime(true) - $totalStartTime, "\n",
+     'Maximum memory usage: ', memory_get_peak_usage(true), "\n";
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php
new file mode 100644
index 00000000000..44700b11586
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php
@@ -0,0 +1,563 @@
+ '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal)
+		{
+			if (strpos($_registered, $key) === FALSE)
+			{
+				continue;
+			}
+
+			foreach (array_keys($$superglobal) as $var)
+			{
+				if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE))
+				{
+					$GLOBALS[$var] = NULL;
+				}
+			}
+		}
+	}
+}
+
+
+/*
+ * ------------------------------------------------------
+ *  Define a custom error handler so we can log PHP errors
+ * ------------------------------------------------------
+ */
+	set_error_handler('_error_handler');
+	set_exception_handler('_exception_handler');
+	register_shutdown_function('_shutdown_handler');
+
+/*
+ * ------------------------------------------------------
+ *  Set the subclass_prefix
+ * ------------------------------------------------------
+ *
+ * Normally the "subclass_prefix" is set in the config file.
+ * The subclass prefix allows CI to know if a core class is
+ * being extended via a library in the local application
+ * "libraries" folder. Since CI allows config items to be
+ * overridden via data set in the main index.php file,
+ * before proceeding we need to know if a subclass_prefix
+ * override exists. If so, we will set this value now,
+ * before any classes are loaded
+ * Note: Since the config file data is cached it doesn't
+ * hurt to load it here.
+ */
+	if ( ! empty($assign_to_config['subclass_prefix']))
+	{
+		get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Should we use a Composer autoloader?
+ * ------------------------------------------------------
+ */
+	if ($composer_autoload = config_item('composer_autoload'))
+	{
+		if ($composer_autoload === TRUE)
+		{
+			file_exists(APPPATH.'vendor/autoload.php')
+				? require_once(APPPATH.'vendor/autoload.php')
+				: log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.');
+		}
+		elseif (file_exists($composer_autoload))
+		{
+			require_once($composer_autoload);
+		}
+		else
+		{
+			log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Start the timer... tick tock tick tock...
+ * ------------------------------------------------------
+ */
+	$BM =& load_class('Benchmark', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('BM', $BM);
+	$BM->mark('total_execution_time_start');
+	$BM->mark('loading_time:_base_classes_start');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the hooks class
+ * ------------------------------------------------------
+ */
+	$EXT =& load_class('Hooks', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('EXT', $EXT);
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_system');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the config class
+ * ------------------------------------------------------
+ *
+ * Note: It is important that Config is loaded first as
+ * most other classes depend on it either directly or by
+ * depending on another class that uses it.
+ *
+ */
+	$CFG =& load_class('Config', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('CFG', $CFG);
+
+	// Do we have any manually set config items in the index.php file?
+	if (isset($assign_to_config) && is_array($assign_to_config))
+	{
+		foreach ($assign_to_config as $key => $value)
+		{
+			$CFG->set_item($key, $value);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ * Important charset-related stuff
+ * ------------------------------------------------------
+ *
+ * Configure mbstring and/or iconv if they are enabled
+ * and set MB_ENABLED and ICONV_ENABLED constants, so
+ * that we don't repeatedly do extension_loaded() or
+ * function_exists() calls.
+ *
+ * Note: UTF-8 class depends on this. It used to be done
+ * in it's constructor, but it's _not_ class-specific.
+ *
+ */
+	$charset = strtoupper(config_item('charset'));
+	ini_set('default_charset', $charset);
+
+	if (extension_loaded('mbstring'))
+	{
+		define('MB_ENABLED', TRUE);
+		// mbstring.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('mbstring.internal_encoding', $charset);
+		// This is required for mb_convert_encoding() to strip invalid characters.
+		// That's utilized by CI_Utf8, but it's also done for consistency with iconv.
+		mb_substitute_character('none');
+	}
+	else
+	{
+		define('MB_ENABLED', FALSE);
+	}
+
+	// There's an ICONV_IMPL constant, but the PHP manual says that using
+	// iconv's predefined constants is "strongly discouraged".
+	if (extension_loaded('iconv'))
+	{
+		define('ICONV_ENABLED', TRUE);
+		// iconv.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('iconv.internal_encoding', $charset);
+	}
+	else
+	{
+		define('ICONV_ENABLED', FALSE);
+	}
+
+	if (is_php('5.6'))
+	{
+		ini_set('php.internal_encoding', $charset);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Load compatibility features
+ * ------------------------------------------------------
+ */
+
+	require_once(BASEPATH.'core/compat/mbstring.php');
+	require_once(BASEPATH.'core/compat/hash.php');
+	require_once(BASEPATH.'core/compat/password.php');
+	require_once(BASEPATH.'core/compat/standard.php');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the UTF-8 class
+ * ------------------------------------------------------
+ */
+	$UNI =& load_class('Utf8', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('UNI', $UNI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the URI class
+ * ------------------------------------------------------
+ */
+	$URI =& load_class('URI', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('URI', $URI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the routing class and set the routing
+ * ------------------------------------------------------
+ */
+	$RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);
+	CIPHPUnitTestSuperGlobal::set_Global('RTR', $RTR);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the output class
+ * ------------------------------------------------------
+ */
+	$OUT =& load_class('Output', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('OUT', $OUT);
+
+/*
+ * ------------------------------------------------------
+ *	Is there a valid cache file? If so, we're done...
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)
+	{
+		exit;
+	}
+
+/*
+ * -----------------------------------------------------
+ * Load the security class for xss and csrf support
+ * -----------------------------------------------------
+ */
+	$SEC =& load_class('Security', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('SEC', $SEC);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Input class and sanitize globals
+ * ------------------------------------------------------
+ */
+	$IN	=& load_class('Input', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('IN', $IN);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Language class
+ * ------------------------------------------------------
+ */
+	$LANG =& load_class('Lang', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('LANG', $LANG);
+
+/*
+ * ------------------------------------------------------
+ *  Load the app controller and local controller
+ * ------------------------------------------------------
+ *
+ */
+	// Load the base controller class
+	require_once BASEPATH.'core/Controller.php';
+
+	/**
+	 * Reference to the CI_Controller method.
+	 *
+	 * Returns current CI instance object
+	 *
+	 * @return CI_Controller
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	function &get_instance()
+	{
+		if (! CIPHPUnitTest::wiredesignzHmvcInstalled())
+		{
+			return CI_Controller::get_instance();
+		}
+		else
+		{
+			return CI::$APP;
+		}
+	}
+
+	if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
+	{
+		require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
+	}
+
+	// Set a mark point for benchmarking
+	$BM->mark('loading_time:_base_classes_end');
+
+	// ci-phpunit-test
+	return;
+
+/*
+ * ------------------------------------------------------
+ *  Sanity checks
+ * ------------------------------------------------------
+ *
+ *  The Router class has already validated the request,
+ *  leaving us with 3 options here:
+ *
+ *	1) an empty class name, if we reached the default
+ *	   controller, but it didn't exist;
+ *	2) a query string which doesn't go through a
+ *	   file_exists() check
+ *	3) a regular request for a non-existing page
+ *
+ *  We handle all of these as a 404 error.
+ *
+ *  Furthermore, none of the methods in the app controller
+ *  or the loader class can be called via the URI, nor can
+ *  controller methods that begin with an underscore.
+ */
+
+	$e404 = FALSE;
+	$class = ucfirst($RTR->class);
+	$method = $RTR->method;
+
+	if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
+	{
+		$e404 = TRUE;
+	}
+	else
+	{
+		require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
+
+		if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
+		{
+			$e404 = TRUE;
+		}
+		elseif (method_exists($class, '_remap'))
+		{
+			$params = array($method, array_slice($URI->rsegments, 2));
+			$method = '_remap';
+		}
+		// WARNING: It appears that there are issues with is_callable() even in PHP 5.2!
+		// Furthermore, there are bug reports and feature/change requests related to it
+		// that make it unreliable to use in this context. Please, DO NOT change this
+		// work-around until a better alternative is available.
+		elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE))
+		{
+			$e404 = TRUE;
+		}
+	}
+
+	if ($e404)
+	{
+		if ( ! empty($RTR->routes['404_override']))
+		{
+			if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
+			{
+				$error_method = 'index';
+			}
+
+			$error_class = ucfirst($error_class);
+
+			if ( ! class_exists($error_class, FALSE))
+			{
+				if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
+					$e404 = ! class_exists($error_class, FALSE);
+				}
+				// Were we in a directory? If so, check for a global override
+				elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$error_class.'.php');
+					if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
+					{
+						$RTR->directory = '';
+					}
+				}
+			}
+			else
+			{
+				$e404 = FALSE;
+			}
+		}
+
+		// Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
+		if ( ! $e404)
+		{
+			$class = $error_class;
+			$method = $error_method;
+
+			$URI->rsegments = array(
+				1 => $class,
+				2 => $method
+			);
+		}
+		else
+		{
+			show_404($RTR->directory.$class.'/'.$method);
+		}
+	}
+
+	if ($method !== '_remap')
+	{
+		$params = array_slice($URI->rsegments, 2);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the requested controller
+ * ------------------------------------------------------
+ */
+	// Mark a start point so we can benchmark the controller
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
+
+	$CI = new $class();
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller_constructor" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller_constructor');
+
+/*
+ * ------------------------------------------------------
+ *  Call the requested method
+ * ------------------------------------------------------
+ */
+	call_user_func_array(array(&$CI, $method), $params);
+
+	// Mark a benchmark end point
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Send the final rendered output to the browser
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('display_override') === FALSE)
+	{
+		$OUT->_display();
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_system');
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.3 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.3
new file mode 100644
index 00000000000..b3760f0bf9b
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.3
@@ -0,0 +1,563 @@
+ '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal)
+		{
+			if (strpos($_registered, $key) === FALSE)
+			{
+				continue;
+			}
+
+			foreach (array_keys($$superglobal) as $var)
+			{
+				if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE))
+				{
+					$GLOBALS[$var] = NULL;
+				}
+			}
+		}
+	}
+}
+
+
+/*
+ * ------------------------------------------------------
+ *  Define a custom error handler so we can log PHP errors
+ * ------------------------------------------------------
+ */
+	set_error_handler('_error_handler');
+	set_exception_handler('_exception_handler');
+	register_shutdown_function('_shutdown_handler');
+
+/*
+ * ------------------------------------------------------
+ *  Set the subclass_prefix
+ * ------------------------------------------------------
+ *
+ * Normally the "subclass_prefix" is set in the config file.
+ * The subclass prefix allows CI to know if a core class is
+ * being extended via a library in the local application
+ * "libraries" folder. Since CI allows config items to be
+ * overridden via data set in the main index.php file,
+ * before proceeding we need to know if a subclass_prefix
+ * override exists. If so, we will set this value now,
+ * before any classes are loaded
+ * Note: Since the config file data is cached it doesn't
+ * hurt to load it here.
+ */
+	if ( ! empty($assign_to_config['subclass_prefix']))
+	{
+		get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Should we use a Composer autoloader?
+ * ------------------------------------------------------
+ */
+	if ($composer_autoload = config_item('composer_autoload'))
+	{
+		if ($composer_autoload === TRUE)
+		{
+			file_exists(APPPATH.'vendor/autoload.php')
+				? require_once(APPPATH.'vendor/autoload.php')
+				: log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.');
+		}
+		elseif (file_exists($composer_autoload))
+		{
+			require_once($composer_autoload);
+		}
+		else
+		{
+			log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Start the timer... tick tock tick tock...
+ * ------------------------------------------------------
+ */
+	$BM =& load_class('Benchmark', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('BM', $BM);
+	$BM->mark('total_execution_time_start');
+	$BM->mark('loading_time:_base_classes_start');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the hooks class
+ * ------------------------------------------------------
+ */
+	$EXT =& load_class('Hooks', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('EXT', $EXT);
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_system');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the config class
+ * ------------------------------------------------------
+ *
+ * Note: It is important that Config is loaded first as
+ * most other classes depend on it either directly or by
+ * depending on another class that uses it.
+ *
+ */
+	$CFG =& load_class('Config', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('CFG', $CFG);
+
+	// Do we have any manually set config items in the index.php file?
+	if (isset($assign_to_config) && is_array($assign_to_config))
+	{
+		foreach ($assign_to_config as $key => $value)
+		{
+			$CFG->set_item($key, $value);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ * Important charset-related stuff
+ * ------------------------------------------------------
+ *
+ * Configure mbstring and/or iconv if they are enabled
+ * and set MB_ENABLED and ICONV_ENABLED constants, so
+ * that we don't repeatedly do extension_loaded() or
+ * function_exists() calls.
+ *
+ * Note: UTF-8 class depends on this. It used to be done
+ * in it's constructor, but it's _not_ class-specific.
+ *
+ */
+	$charset = strtoupper(config_item('charset'));
+	ini_set('default_charset', $charset);
+
+	if (extension_loaded('mbstring'))
+	{
+		define('MB_ENABLED', TRUE);
+		// mbstring.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('mbstring.internal_encoding', $charset);
+		// This is required for mb_convert_encoding() to strip invalid characters.
+		// That's utilized by CI_Utf8, but it's also done for consistency with iconv.
+		mb_substitute_character('none');
+	}
+	else
+	{
+		define('MB_ENABLED', FALSE);
+	}
+
+	// There's an ICONV_IMPL constant, but the PHP manual says that using
+	// iconv's predefined constants is "strongly discouraged".
+	if (extension_loaded('iconv'))
+	{
+		define('ICONV_ENABLED', TRUE);
+		// iconv.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('iconv.internal_encoding', $charset);
+	}
+	else
+	{
+		define('ICONV_ENABLED', FALSE);
+	}
+
+	if (is_php('5.6'))
+	{
+		ini_set('php.internal_encoding', $charset);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Load compatibility features
+ * ------------------------------------------------------
+ */
+
+	require_once(BASEPATH.'core/compat/mbstring.php');
+	require_once(BASEPATH.'core/compat/hash.php');
+	require_once(BASEPATH.'core/compat/password.php');
+	require_once(BASEPATH.'core/compat/standard.php');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the UTF-8 class
+ * ------------------------------------------------------
+ */
+	$UNI =& load_class('Utf8', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('UNI', $UNI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the URI class
+ * ------------------------------------------------------
+ */
+	$URI =& load_class('URI', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('URI', $URI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the routing class and set the routing
+ * ------------------------------------------------------
+ */
+	$RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);
+	CIPHPUnitTestSuperGlobal::set_Global('RTR', $RTR);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the output class
+ * ------------------------------------------------------
+ */
+	$OUT =& load_class('Output', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('OUT', $OUT);
+
+/*
+ * ------------------------------------------------------
+ *	Is there a valid cache file? If so, we're done...
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)
+	{
+		exit;
+	}
+
+/*
+ * -----------------------------------------------------
+ * Load the security class for xss and csrf support
+ * -----------------------------------------------------
+ */
+	$SEC =& load_class('Security', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('SEC', $SEC);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Input class and sanitize globals
+ * ------------------------------------------------------
+ */
+	$IN	=& load_class('Input', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('IN', $IN);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Language class
+ * ------------------------------------------------------
+ */
+	$LANG =& load_class('Lang', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('LANG', $LANG);
+
+/*
+ * ------------------------------------------------------
+ *  Load the app controller and local controller
+ * ------------------------------------------------------
+ *
+ */
+	// Load the base controller class
+	require_once BASEPATH.'core/Controller.php';
+
+	/**
+	 * Reference to the CI_Controller method.
+	 *
+	 * Returns current CI instance object
+	 *
+	 * @return object
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	function &get_instance()
+	{
+		if (! CIPHPUnitTest::wiredesignzHmvcInstalled())
+		{
+			return CI_Controller::get_instance();
+		}
+		else
+		{
+			return CI::$APP;
+		}
+	}
+
+	if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
+	{
+		require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
+	}
+
+	// Set a mark point for benchmarking
+	$BM->mark('loading_time:_base_classes_end');
+
+	// ci-phpunit-test
+	return;
+
+/*
+ * ------------------------------------------------------
+ *  Sanity checks
+ * ------------------------------------------------------
+ *
+ *  The Router class has already validated the request,
+ *  leaving us with 3 options here:
+ *
+ *	1) an empty class name, if we reached the default
+ *	   controller, but it didn't exist;
+ *	2) a query string which doesn't go through a
+ *	   file_exists() check
+ *	3) a regular request for a non-existing page
+ *
+ *  We handle all of these as a 404 error.
+ *
+ *  Furthermore, none of the methods in the app controller
+ *  or the loader class can be called via the URI, nor can
+ *  controller methods that begin with an underscore.
+ */
+
+	$e404 = FALSE;
+	$class = ucfirst($RTR->class);
+	$method = $RTR->method;
+
+	if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
+	{
+		$e404 = TRUE;
+	}
+	else
+	{
+		require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
+
+		if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
+		{
+			$e404 = TRUE;
+		}
+		elseif (method_exists($class, '_remap'))
+		{
+			$params = array($method, array_slice($URI->rsegments, 2));
+			$method = '_remap';
+		}
+		// WARNING: It appears that there are issues with is_callable() even in PHP 5.2!
+		// Furthermore, there are bug reports and feature/change requests related to it
+		// that make it unreliable to use in this context. Please, DO NOT change this
+		// work-around until a better alternative is available.
+		elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE))
+		{
+			$e404 = TRUE;
+		}
+	}
+
+	if ($e404)
+	{
+		if ( ! empty($RTR->routes['404_override']))
+		{
+			if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
+			{
+				$error_method = 'index';
+			}
+
+			$error_class = ucfirst($error_class);
+
+			if ( ! class_exists($error_class, FALSE))
+			{
+				if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
+					$e404 = ! class_exists($error_class, FALSE);
+				}
+				// Were we in a directory? If so, check for a global override
+				elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$error_class.'.php');
+					if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
+					{
+						$RTR->directory = '';
+					}
+				}
+			}
+			else
+			{
+				$e404 = FALSE;
+			}
+		}
+
+		// Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
+		if ( ! $e404)
+		{
+			$class = $error_class;
+			$method = $error_method;
+
+			$URI->rsegments = array(
+				1 => $class,
+				2 => $method
+			);
+		}
+		else
+		{
+			show_404($RTR->directory.$class.'/'.$method);
+		}
+	}
+
+	if ($method !== '_remap')
+	{
+		$params = array_slice($URI->rsegments, 2);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the requested controller
+ * ------------------------------------------------------
+ */
+	// Mark a start point so we can benchmark the controller
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
+
+	$CI = new $class();
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller_constructor" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller_constructor');
+
+/*
+ * ------------------------------------------------------
+ *  Call the requested method
+ * ------------------------------------------------------
+ */
+	call_user_func_array(array(&$CI, $method), $params);
+
+	// Mark a benchmark end point
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Send the final rendered output to the browser
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('display_override') === FALSE)
+	{
+		$OUT->_display();
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_system');
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.4 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.4
new file mode 100644
index 00000000000..5df0a770d47
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.4
@@ -0,0 +1,563 @@
+ '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal)
+		{
+			if (strpos($_registered, $key) === FALSE)
+			{
+				continue;
+			}
+
+			foreach (array_keys($$superglobal) as $var)
+			{
+				if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE))
+				{
+					$GLOBALS[$var] = NULL;
+				}
+			}
+		}
+	}
+}
+
+
+/*
+ * ------------------------------------------------------
+ *  Define a custom error handler so we can log PHP errors
+ * ------------------------------------------------------
+ */
+	set_error_handler('_error_handler');
+	set_exception_handler('_exception_handler');
+	register_shutdown_function('_shutdown_handler');
+
+/*
+ * ------------------------------------------------------
+ *  Set the subclass_prefix
+ * ------------------------------------------------------
+ *
+ * Normally the "subclass_prefix" is set in the config file.
+ * The subclass prefix allows CI to know if a core class is
+ * being extended via a library in the local application
+ * "libraries" folder. Since CI allows config items to be
+ * overridden via data set in the main index.php file,
+ * before proceeding we need to know if a subclass_prefix
+ * override exists. If so, we will set this value now,
+ * before any classes are loaded
+ * Note: Since the config file data is cached it doesn't
+ * hurt to load it here.
+ */
+	if ( ! empty($assign_to_config['subclass_prefix']))
+	{
+		get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Should we use a Composer autoloader?
+ * ------------------------------------------------------
+ */
+	if ($composer_autoload = config_item('composer_autoload'))
+	{
+		if ($composer_autoload === TRUE)
+		{
+			file_exists(APPPATH.'vendor/autoload.php')
+				? require_once(APPPATH.'vendor/autoload.php')
+				: log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.');
+		}
+		elseif (file_exists($composer_autoload))
+		{
+			require_once($composer_autoload);
+		}
+		else
+		{
+			log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Start the timer... tick tock tick tock...
+ * ------------------------------------------------------
+ */
+	$BM =& load_class('Benchmark', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('BM', $BM);
+	$BM->mark('total_execution_time_start');
+	$BM->mark('loading_time:_base_classes_start');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the hooks class
+ * ------------------------------------------------------
+ */
+	$EXT =& load_class('Hooks', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('EXT', $EXT);
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_system');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the config class
+ * ------------------------------------------------------
+ *
+ * Note: It is important that Config is loaded first as
+ * most other classes depend on it either directly or by
+ * depending on another class that uses it.
+ *
+ */
+	$CFG =& load_class('Config', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('CFG', $CFG);
+
+	// Do we have any manually set config items in the index.php file?
+	if (isset($assign_to_config) && is_array($assign_to_config))
+	{
+		foreach ($assign_to_config as $key => $value)
+		{
+			$CFG->set_item($key, $value);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ * Important charset-related stuff
+ * ------------------------------------------------------
+ *
+ * Configure mbstring and/or iconv if they are enabled
+ * and set MB_ENABLED and ICONV_ENABLED constants, so
+ * that we don't repeatedly do extension_loaded() or
+ * function_exists() calls.
+ *
+ * Note: UTF-8 class depends on this. It used to be done
+ * in it's constructor, but it's _not_ class-specific.
+ *
+ */
+	$charset = strtoupper(config_item('charset'));
+	ini_set('default_charset', $charset);
+
+	if (extension_loaded('mbstring'))
+	{
+		define('MB_ENABLED', TRUE);
+		// mbstring.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('mbstring.internal_encoding', $charset);
+		// This is required for mb_convert_encoding() to strip invalid characters.
+		// That's utilized by CI_Utf8, but it's also done for consistency with iconv.
+		mb_substitute_character('none');
+	}
+	else
+	{
+		define('MB_ENABLED', FALSE);
+	}
+
+	// There's an ICONV_IMPL constant, but the PHP manual says that using
+	// iconv's predefined constants is "strongly discouraged".
+	if (extension_loaded('iconv'))
+	{
+		define('ICONV_ENABLED', TRUE);
+		// iconv.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('iconv.internal_encoding', $charset);
+	}
+	else
+	{
+		define('ICONV_ENABLED', FALSE);
+	}
+
+	if (is_php('5.6'))
+	{
+		ini_set('php.internal_encoding', $charset);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Load compatibility features
+ * ------------------------------------------------------
+ */
+
+	require_once(BASEPATH.'core/compat/mbstring.php');
+	require_once(BASEPATH.'core/compat/hash.php');
+	require_once(BASEPATH.'core/compat/password.php');
+	require_once(BASEPATH.'core/compat/standard.php');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the UTF-8 class
+ * ------------------------------------------------------
+ */
+	$UNI =& load_class('Utf8', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('UNI', $UNI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the URI class
+ * ------------------------------------------------------
+ */
+	$URI =& load_class('URI', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('URI', $URI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the routing class and set the routing
+ * ------------------------------------------------------
+ */
+	$RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);
+	CIPHPUnitTestSuperGlobal::set_Global('RTR', $RTR);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the output class
+ * ------------------------------------------------------
+ */
+	$OUT =& load_class('Output', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('OUT', $OUT);
+
+/*
+ * ------------------------------------------------------
+ *	Is there a valid cache file? If so, we're done...
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)
+	{
+		exit;
+	}
+
+/*
+ * -----------------------------------------------------
+ * Load the security class for xss and csrf support
+ * -----------------------------------------------------
+ */
+	$SEC =& load_class('Security', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('SEC', $SEC);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Input class and sanitize globals
+ * ------------------------------------------------------
+ */
+	$IN	=& load_class('Input', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('IN', $IN);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Language class
+ * ------------------------------------------------------
+ */
+	$LANG =& load_class('Lang', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('LANG', $LANG);
+
+/*
+ * ------------------------------------------------------
+ *  Load the app controller and local controller
+ * ------------------------------------------------------
+ *
+ */
+	// Load the base controller class
+	require_once BASEPATH.'core/Controller.php';
+
+	/**
+	 * Reference to the CI_Controller method.
+	 *
+	 * Returns current CI instance object
+	 *
+	 * @return object
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	function &get_instance()
+	{
+		if (! CIPHPUnitTest::wiredesignzHmvcInstalled())
+		{
+			return CI_Controller::get_instance();
+		}
+		else
+		{
+			return CI::$APP;
+		}
+	}
+
+	if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
+	{
+		require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
+	}
+
+	// Set a mark point for benchmarking
+	$BM->mark('loading_time:_base_classes_end');
+
+	// ci-phpunit-test
+	return;
+
+/*
+ * ------------------------------------------------------
+ *  Sanity checks
+ * ------------------------------------------------------
+ *
+ *  The Router class has already validated the request,
+ *  leaving us with 3 options here:
+ *
+ *	1) an empty class name, if we reached the default
+ *	   controller, but it didn't exist;
+ *	2) a query string which doesn't go through a
+ *	   file_exists() check
+ *	3) a regular request for a non-existing page
+ *
+ *  We handle all of these as a 404 error.
+ *
+ *  Furthermore, none of the methods in the app controller
+ *  or the loader class can be called via the URI, nor can
+ *  controller methods that begin with an underscore.
+ */
+
+	$e404 = FALSE;
+	$class = ucfirst($RTR->class);
+	$method = $RTR->method;
+
+	if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
+	{
+		$e404 = TRUE;
+	}
+	else
+	{
+		require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
+
+		if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
+		{
+			$e404 = TRUE;
+		}
+		elseif (method_exists($class, '_remap'))
+		{
+			$params = array($method, array_slice($URI->rsegments, 2));
+			$method = '_remap';
+		}
+		// WARNING: It appears that there are issues with is_callable() even in PHP 5.2!
+		// Furthermore, there are bug reports and feature/change requests related to it
+		// that make it unreliable to use in this context. Please, DO NOT change this
+		// work-around until a better alternative is available.
+		elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE))
+		{
+			$e404 = TRUE;
+		}
+	}
+
+	if ($e404)
+	{
+		if ( ! empty($RTR->routes['404_override']))
+		{
+			if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
+			{
+				$error_method = 'index';
+			}
+
+			$error_class = ucfirst($error_class);
+
+			if ( ! class_exists($error_class, FALSE))
+			{
+				if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
+					$e404 = ! class_exists($error_class, FALSE);
+				}
+				// Were we in a directory? If so, check for a global override
+				elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$error_class.'.php');
+					if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
+					{
+						$RTR->directory = '';
+					}
+				}
+			}
+			else
+			{
+				$e404 = FALSE;
+			}
+		}
+
+		// Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
+		if ( ! $e404)
+		{
+			$class = $error_class;
+			$method = $error_method;
+
+			$URI->rsegments = array(
+				1 => $class,
+				2 => $method
+			);
+		}
+		else
+		{
+			show_404($RTR->directory.$class.'/'.$method);
+		}
+	}
+
+	if ($method !== '_remap')
+	{
+		$params = array_slice($URI->rsegments, 2);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the requested controller
+ * ------------------------------------------------------
+ */
+	// Mark a start point so we can benchmark the controller
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
+
+	$CI = new $class();
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller_constructor" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller_constructor');
+
+/*
+ * ------------------------------------------------------
+ *  Call the requested method
+ * ------------------------------------------------------
+ */
+	call_user_func_array(array(&$CI, $method), $params);
+
+	// Mark a benchmark end point
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Send the final rendered output to the browser
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('display_override') === FALSE)
+	{
+		$OUT->_display();
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_system');
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.5 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.5
new file mode 100644
index 00000000000..13c7b6ea684
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php-3.0.5
@@ -0,0 +1,563 @@
+ '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal)
+		{
+			if (strpos($_registered, $key) === FALSE)
+			{
+				continue;
+			}
+
+			foreach (array_keys($$superglobal) as $var)
+			{
+				if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE))
+				{
+					$GLOBALS[$var] = NULL;
+				}
+			}
+		}
+	}
+}
+
+
+/*
+ * ------------------------------------------------------
+ *  Define a custom error handler so we can log PHP errors
+ * ------------------------------------------------------
+ */
+	set_error_handler('_error_handler');
+	set_exception_handler('_exception_handler');
+	register_shutdown_function('_shutdown_handler');
+
+/*
+ * ------------------------------------------------------
+ *  Set the subclass_prefix
+ * ------------------------------------------------------
+ *
+ * Normally the "subclass_prefix" is set in the config file.
+ * The subclass prefix allows CI to know if a core class is
+ * being extended via a library in the local application
+ * "libraries" folder. Since CI allows config items to be
+ * overridden via data set in the main index.php file,
+ * before proceeding we need to know if a subclass_prefix
+ * override exists. If so, we will set this value now,
+ * before any classes are loaded
+ * Note: Since the config file data is cached it doesn't
+ * hurt to load it here.
+ */
+	if ( ! empty($assign_to_config['subclass_prefix']))
+	{
+		get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Should we use a Composer autoloader?
+ * ------------------------------------------------------
+ */
+	if ($composer_autoload = config_item('composer_autoload'))
+	{
+		if ($composer_autoload === TRUE)
+		{
+			file_exists(APPPATH.'vendor/autoload.php')
+				? require_once(APPPATH.'vendor/autoload.php')
+				: log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.');
+		}
+		elseif (file_exists($composer_autoload))
+		{
+			require_once($composer_autoload);
+		}
+		else
+		{
+			log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Start the timer... tick tock tick tock...
+ * ------------------------------------------------------
+ */
+	$BM =& load_class('Benchmark', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('BM', $BM);
+	$BM->mark('total_execution_time_start');
+	$BM->mark('loading_time:_base_classes_start');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the hooks class
+ * ------------------------------------------------------
+ */
+	$EXT =& load_class('Hooks', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('EXT', $EXT);
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_system');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the config class
+ * ------------------------------------------------------
+ *
+ * Note: It is important that Config is loaded first as
+ * most other classes depend on it either directly or by
+ * depending on another class that uses it.
+ *
+ */
+	$CFG =& load_class('Config', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('CFG', $CFG);
+
+	// Do we have any manually set config items in the index.php file?
+	if (isset($assign_to_config) && is_array($assign_to_config))
+	{
+		foreach ($assign_to_config as $key => $value)
+		{
+			$CFG->set_item($key, $value);
+		}
+	}
+
+/*
+ * ------------------------------------------------------
+ * Important charset-related stuff
+ * ------------------------------------------------------
+ *
+ * Configure mbstring and/or iconv if they are enabled
+ * and set MB_ENABLED and ICONV_ENABLED constants, so
+ * that we don't repeatedly do extension_loaded() or
+ * function_exists() calls.
+ *
+ * Note: UTF-8 class depends on this. It used to be done
+ * in it's constructor, but it's _not_ class-specific.
+ *
+ */
+	$charset = strtoupper(config_item('charset'));
+	ini_set('default_charset', $charset);
+
+	if (extension_loaded('mbstring'))
+	{
+		define('MB_ENABLED', TRUE);
+		// mbstring.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('mbstring.internal_encoding', $charset);
+		// This is required for mb_convert_encoding() to strip invalid characters.
+		// That's utilized by CI_Utf8, but it's also done for consistency with iconv.
+		mb_substitute_character('none');
+	}
+	else
+	{
+		define('MB_ENABLED', FALSE);
+	}
+
+	// There's an ICONV_IMPL constant, but the PHP manual says that using
+	// iconv's predefined constants is "strongly discouraged".
+	if (extension_loaded('iconv'))
+	{
+		define('ICONV_ENABLED', TRUE);
+		// iconv.internal_encoding is deprecated starting with PHP 5.6
+		// and it's usage triggers E_DEPRECATED messages.
+		@ini_set('iconv.internal_encoding', $charset);
+	}
+	else
+	{
+		define('ICONV_ENABLED', FALSE);
+	}
+
+	if (is_php('5.6'))
+	{
+		ini_set('php.internal_encoding', $charset);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Load compatibility features
+ * ------------------------------------------------------
+ */
+
+	require_once(BASEPATH.'core/compat/mbstring.php');
+	require_once(BASEPATH.'core/compat/hash.php');
+	require_once(BASEPATH.'core/compat/password.php');
+	require_once(BASEPATH.'core/compat/standard.php');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the UTF-8 class
+ * ------------------------------------------------------
+ */
+	$UNI =& load_class('Utf8', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('UNI', $UNI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the URI class
+ * ------------------------------------------------------
+ */
+	$URI =& load_class('URI', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('URI', $URI);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the routing class and set the routing
+ * ------------------------------------------------------
+ */
+	$RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);
+	CIPHPUnitTestSuperGlobal::set_Global('RTR', $RTR);
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the output class
+ * ------------------------------------------------------
+ */
+	$OUT =& load_class('Output', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('OUT', $OUT);
+
+/*
+ * ------------------------------------------------------
+ *	Is there a valid cache file? If so, we're done...
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)
+	{
+		exit;
+	}
+
+/*
+ * -----------------------------------------------------
+ * Load the security class for xss and csrf support
+ * -----------------------------------------------------
+ */
+	$SEC =& load_class('Security', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('SEC', $SEC);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Input class and sanitize globals
+ * ------------------------------------------------------
+ */
+	$IN	=& load_class('Input', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('IN', $IN);
+
+/*
+ * ------------------------------------------------------
+ *  Load the Language class
+ * ------------------------------------------------------
+ */
+	$LANG =& load_class('Lang', 'core');
+	CIPHPUnitTestSuperGlobal::set_Global('LANG', $LANG);
+
+/*
+ * ------------------------------------------------------
+ *  Load the app controller and local controller
+ * ------------------------------------------------------
+ *
+ */
+	// Load the base controller class
+	require_once BASEPATH.'core/Controller.php';
+
+	/**
+	 * Reference to the CI_Controller method.
+	 *
+	 * Returns current CI instance object
+	 *
+	 * @return CI_Controller
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	function &get_instance()
+	{
+		if (! CIPHPUnitTest::wiredesignzHmvcInstalled())
+		{
+			return CI_Controller::get_instance();
+		}
+		else
+		{
+			return CI::$APP;
+		}
+	}
+
+	if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
+	{
+		require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
+	}
+
+	// Set a mark point for benchmarking
+	$BM->mark('loading_time:_base_classes_end');
+
+	// ci-phpunit-test
+	return;
+
+/*
+ * ------------------------------------------------------
+ *  Sanity checks
+ * ------------------------------------------------------
+ *
+ *  The Router class has already validated the request,
+ *  leaving us with 3 options here:
+ *
+ *	1) an empty class name, if we reached the default
+ *	   controller, but it didn't exist;
+ *	2) a query string which doesn't go through a
+ *	   file_exists() check
+ *	3) a regular request for a non-existing page
+ *
+ *  We handle all of these as a 404 error.
+ *
+ *  Furthermore, none of the methods in the app controller
+ *  or the loader class can be called via the URI, nor can
+ *  controller methods that begin with an underscore.
+ */
+
+	$e404 = FALSE;
+	$class = ucfirst($RTR->class);
+	$method = $RTR->method;
+
+	if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
+	{
+		$e404 = TRUE;
+	}
+	else
+	{
+		require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
+
+		if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
+		{
+			$e404 = TRUE;
+		}
+		elseif (method_exists($class, '_remap'))
+		{
+			$params = array($method, array_slice($URI->rsegments, 2));
+			$method = '_remap';
+		}
+		// WARNING: It appears that there are issues with is_callable() even in PHP 5.2!
+		// Furthermore, there are bug reports and feature/change requests related to it
+		// that make it unreliable to use in this context. Please, DO NOT change this
+		// work-around until a better alternative is available.
+		elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE))
+		{
+			$e404 = TRUE;
+		}
+	}
+
+	if ($e404)
+	{
+		if ( ! empty($RTR->routes['404_override']))
+		{
+			if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
+			{
+				$error_method = 'index';
+			}
+
+			$error_class = ucfirst($error_class);
+
+			if ( ! class_exists($error_class, FALSE))
+			{
+				if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
+					$e404 = ! class_exists($error_class, FALSE);
+				}
+				// Were we in a directory? If so, check for a global override
+				elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
+				{
+					require_once(APPPATH.'controllers/'.$error_class.'.php');
+					if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
+					{
+						$RTR->directory = '';
+					}
+				}
+			}
+			else
+			{
+				$e404 = FALSE;
+			}
+		}
+
+		// Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
+		if ( ! $e404)
+		{
+			$class = $error_class;
+			$method = $error_method;
+
+			$URI->rsegments = array(
+				1 => $class,
+				2 => $method
+			);
+		}
+		else
+		{
+			show_404($RTR->directory.$class.'/'.$method);
+		}
+	}
+
+	if ($method !== '_remap')
+	{
+		$params = array_slice($URI->rsegments, 2);
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "pre_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('pre_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Instantiate the requested controller
+ * ------------------------------------------------------
+ */
+	// Mark a start point so we can benchmark the controller
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
+
+	$CI = new $class();
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller_constructor" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller_constructor');
+
+/*
+ * ------------------------------------------------------
+ *  Call the requested method
+ * ------------------------------------------------------
+ */
+	call_user_func_array(array(&$CI, $method), $params);
+
+	// Mark a benchmark end point
+	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_controller" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_controller');
+
+/*
+ * ------------------------------------------------------
+ *  Send the final rendered output to the browser
+ * ------------------------------------------------------
+ */
+	if ($EXT->call_hook('display_override') === FALSE)
+	{
+		$OUT->_display();
+	}
+
+/*
+ * ------------------------------------------------------
+ *  Is there a "post_system" hook?
+ * ------------------------------------------------------
+ */
+	$EXT->call_hook('post_system');
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Common.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Common.php
new file mode 100644
index 00000000000..ee786f277d2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Common.php
@@ -0,0 +1,383 @@
+
+ * @license    MIT License
+ * @copyright  2015 Kenji Suzuki
+ * @link       https://github.com/kenjis/ci-phpunit-test
+ */
+
+// Load this file before loading "system/core/Common.php"
+
+/**
+ * Class registry
+ * 
+ * @staticvar array $_classes
+ * 
+ * @param string $class
+ * @param string $directory
+ * @param array  $param
+ * @param bool   $reset
+ * @param object $obj
+ * 
+ * @return object
+ */
+function &load_class(
+	$class,
+	$directory = 'libraries',
+	$param = NULL,
+	$reset = FALSE,
+	$obj = NULL
+)
+{
+	static $_classes = array();
+
+	if ($reset)
+	{
+		// If Utf8 is instantiated twice,
+		// error "Constant UTF8_ENABLED already defined" occurs
+		$UTF8 = $_classes['Utf8'];
+		$_classes = array(
+			'Utf8' => $UTF8
+		);
+		$obj = new stdClass();
+		return $obj;
+	}
+
+	// Register object directly
+	if ($obj)
+	{
+		is_loaded($class);
+
+		$_classes[$class] = $obj;
+		return $_classes[$class];
+	}
+
+	// Does the class exist? If so, we're done...
+	if (isset($_classes[$class]))
+	{
+		return $_classes[$class];
+	}
+
+	$name = FALSE;
+
+	// Look for the class first in the local application/libraries folder
+	// then in the native system/libraries folder
+	foreach (array(APPPATH, BASEPATH) as $path)
+	{
+		if (file_exists($path.$directory.'/'.$class.'.php'))
+		{
+			$name = 'CI_'.$class;
+
+			if (class_exists($name, FALSE) === FALSE)
+			{
+				require_once($path.$directory.'/'.$class.'.php');
+			}
+
+			break;
+		}
+	}
+
+	// Is the request a class extension? If so we load it too
+	if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'))
+	{
+		$name = config_item('subclass_prefix').$class;
+
+		if (class_exists($name, FALSE) === FALSE)
+		{
+			require_once(APPPATH.$directory.'/'.$name.'.php');
+		}
+	}
+
+	// Did we find the class?
+	if ($name === FALSE)
+	{
+		// Note: We use exit() rather then show_error() in order to avoid a
+		// self-referencing loop with the Exceptions class
+		set_status_header(503);
+
+		// changed by ci-phpunit-test
+		$msg = 'Unable to locate the specified class: '.$class.'.php';
+//		exit(5); // EXIT_UNK_CLASS
+		throw new CIPHPUnitTestExitException($msg);
+	}
+
+	// Keep track of what we just loaded
+	is_loaded($class);
+
+	$_classes[$class] = isset($param)
+		? new $name($param)
+		: new $name();
+	return $_classes[$class];
+}
+
+/**
+ * Keeps track of which libraries have been loaded.
+ * 
+ * @staticvar array $_is_loaded
+ * 
+ * @param string $class
+ * @param bool   $reset
+ * 
+ * @return array
+ */
+function &is_loaded($class = '', $reset = FALSE)
+{
+	static $_is_loaded = array();
+
+	if ($reset)
+	{
+		$_is_loaded = array();
+		return $_is_loaded;
+	}
+
+	if ($class !== '')
+	{
+		$_is_loaded[strtolower($class)] = $class;
+	}
+
+	return $_is_loaded;
+}
+
+function is_cli($return = null)
+{
+	static $_return = TRUE;
+
+	if ($return !== null)
+	{
+		$_return = $return;
+	}
+
+	return $_return;
+}
+
+function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered')
+{
+	$status_code = abs($status_code);
+	if ($status_code < 100)
+	{
+		$exit_status = $status_code + 9; // 9 is EXIT__AUTO_MIN
+		if ($exit_status > 125) // 125 is EXIT__AUTO_MAX
+		{
+			$exit_status = 1; // EXIT_ERROR
+		}
+
+		$status_code = 500;
+	}
+	else
+	{
+		$exit_status = 1; // EXIT_ERROR
+	}
+
+	while (ob_get_level() > 1)
+	{
+		ob_end_clean();
+	}
+
+	throw new CIPHPUnitTestShowErrorException($message, $status_code);
+}
+
+function show_404($page = '', $log_error = TRUE)
+{
+	while (ob_get_level() > 1)
+	{
+		ob_end_clean();
+	}
+
+	throw new CIPHPUnitTestShow404Exception($page, 404);
+}
+
+function set_status_header($code = 200, $text = '')
+{
+//	if (is_cli())
+//	{
+//		return;
+//	}
+
+	if (empty($code) OR ! is_numeric($code))
+	{
+		show_error('Status codes must be numeric', 500);
+	}
+
+	if (empty($text))
+	{
+		is_int($code) OR $code = (int) $code;
+		$stati = array(
+			100	=> 'Continue',
+			101	=> 'Switching Protocols',
+
+			200	=> 'OK',
+			201	=> 'Created',
+			202	=> 'Accepted',
+			203	=> 'Non-Authoritative Information',
+			204	=> 'No Content',
+			205	=> 'Reset Content',
+			206	=> 'Partial Content',
+
+			300	=> 'Multiple Choices',
+			301	=> 'Moved Permanently',
+			302	=> 'Found',
+			303	=> 'See Other',
+			304	=> 'Not Modified',
+			305	=> 'Use Proxy',
+			307	=> 'Temporary Redirect',
+
+			400	=> 'Bad Request',
+			401	=> 'Unauthorized',
+			402	=> 'Payment Required',
+			403	=> 'Forbidden',
+			404	=> 'Not Found',
+			405	=> 'Method Not Allowed',
+			406	=> 'Not Acceptable',
+			407	=> 'Proxy Authentication Required',
+			408	=> 'Request Timeout',
+			409	=> 'Conflict',
+			410	=> 'Gone',
+			411	=> 'Length Required',
+			412	=> 'Precondition Failed',
+			413	=> 'Request Entity Too Large',
+			414	=> 'Request-URI Too Long',
+			415	=> 'Unsupported Media Type',
+			416	=> 'Requested Range Not Satisfiable',
+			417	=> 'Expectation Failed',
+			422	=> 'Unprocessable Entity',
+
+			500	=> 'Internal Server Error',
+			501	=> 'Not Implemented',
+			502	=> 'Bad Gateway',
+			503	=> 'Service Unavailable',
+			504	=> 'Gateway Timeout',
+			505	=> 'HTTP Version Not Supported'
+		);
+
+		if (isset($stati[$code]))
+		{
+			$text = $stati[$code];
+		}
+		else
+		{
+			show_error('No status text available. Please check your status code number or supply your own message text.', 500);
+		}
+	}
+
+	// Save status code in Output object
+	// added by ci-phpunit-test
+	$CI =& get_instance();
+	$output = $CI->output;
+	$output->_status = [
+		'code'     => $code,
+		'text'     => $text,
+		'redirect' => null,
+	];
+
+	// Everything is done, so return
+	// added by ci-phpunit-test
+	if (ENVIRONMENT === 'testing')
+	{
+		return;
+	}
+
+	if (strpos(PHP_SAPI, 'cgi') === 0)
+	{
+		header('Status: '.$code.' '.$text, TRUE);
+	}
+	else
+	{
+		$server_protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
+		header($server_protocol.' '.$code.' '.$text, TRUE, $code);
+	}
+}
+
+/**
+ * Loads the main config.php file
+ * 
+ * @staticvar array $config
+ * 
+ * @param array $replace
+ * @param bool  $reset
+ * 
+ * @return array
+ */
+function &get_config(Array $replace = array(), $reset = FALSE)
+{
+	static $config;
+
+	// Reset static variable
+	// added by ci-phpunit-test
+	if ($reset)
+	{
+		$config = null;
+		return $config;
+	}
+
+	if (empty($config))
+	{
+		$file_path = APPPATH.'config/config.php';
+		$found = FALSE;
+		if (file_exists($file_path))
+		{
+			$found = TRUE;
+			require($file_path);
+		}
+
+		// Is the config file in the environment folder?
+		if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
+		{
+			require($file_path);
+		}
+		elseif ( ! $found)
+		{
+			set_status_header(503);
+			echo 'The configuration file does not exist.';
+			exit(3); // EXIT_CONFIG
+		}
+
+		// Does the $config array exist in the file?
+		if ( ! isset($config) OR ! is_array($config))
+		{
+			set_status_header(503);
+			echo 'Your config file does not appear to be formatted correctly.';
+			exit(3); // EXIT_CONFIG
+		}
+	}
+
+	// Are any values being dynamically added or replaced?
+	foreach ($replace as $key => $val)
+	{
+		$config[$key] = $val;
+	}
+
+	return $config;
+}
+
+/**
+ * Returns the specified config item
+ * 
+ * @staticvar array $_config
+ * 
+ * @param string $item
+ * @param bool   $reset
+ * 
+ * @return type
+ */
+function config_item($item, $reset = FALSE)
+{
+	static $_config;
+
+	// Reset static variable
+	// added by ci-phpunit-test
+	if ($reset)
+	{
+		$config = null;
+		return;
+	}
+
+	if (empty($_config))
+	{
+		// references cannot be directly assigned to static variables, so we use an array
+		$_config[0] =& get_config();
+	}
+
+	return isset($_config[0][$item]) ? $_config[0][$item] : NULL;
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Input.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Input.php
new file mode 100644
index 00000000000..ab1f8f4a629
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Input.php
@@ -0,0 +1,912 @@
+_allow_get_array		= (config_item('allow_get_array') === TRUE);
+		$this->_enable_xss		= (config_item('global_xss_filtering') === TRUE);
+		$this->_enable_csrf		= (config_item('csrf_protection') === TRUE);
+		$this->_standardize_newlines	= (bool) config_item('standardize_newlines');
+
+		$this->security =& load_class('Security', 'core');
+
+		// Do we need the UTF-8 class?
+		if (UTF8_ENABLED === TRUE)
+		{
+			$this->uni =& load_class('Utf8', 'core');
+		}
+
+		// Sanitize global arrays
+		$this->_sanitize_globals();
+
+		// CSRF Protection check
+		if ($this->_enable_csrf === TRUE && ! is_cli())
+		{
+			$this->security->csrf_verify();
+		}
+
+		log_message('info', 'Input Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch from array
+	 *
+	 * Internal method used to retrieve values from global arrays.
+	 *
+	 * @param	array	&$array		$_GET, $_POST, $_COOKIE, $_SERVER, etc.
+	 * @param	mixed	$index		Index for item to be fetched from $array
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL)
+	{
+		is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
+		// If $index is NULL, it means that the whole $array is requested
+		isset($index) OR $index = array_keys($array);
+
+		// allow fetching multiple keys at once
+		if (is_array($index))
+		{
+			$output = array();
+			foreach ($index as $key)
+			{
+				$output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);
+			}
+
+			return $output;
+		}
+
+		if (isset($array[$index]))
+		{
+			$value = $array[$index];
+		}
+		elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation
+		{
+			$value = $array;
+			for ($i = 0; $i < $count; $i++)
+			{
+				$key = trim($matches[0][$i], '[]');
+				if ($key === '') // Empty notation will return the value as array
+				{
+					break;
+				}
+
+				if (isset($value[$key]))
+				{
+					$value = $value[$key];
+				}
+				else
+				{
+					return NULL;
+				}
+			}
+		}
+		else
+		{
+			return NULL;
+		}
+
+		return ($xss_clean === TRUE)
+			? $this->security->xss_clean($value)
+			: $value;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the GET array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_GET
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function get($index = NULL, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_GET, $index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the POST array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_POST
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function post($index = NULL, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_POST, $index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from POST data with fallback to GET
+	 *
+	 * @param	string	$index		Index for item to be fetched from $_POST or $_GET
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function post_get($index, $xss_clean = NULL)
+	{
+		return isset($_POST[$index])
+			? $this->post($index, $xss_clean)
+			: $this->get($index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from GET data with fallback to POST
+	 *
+	 * @param	string	$index		Index for item to be fetched from $_GET or $_POST
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function get_post($index, $xss_clean = NULL)
+	{
+		return isset($_GET[$index])
+			? $this->get($index, $xss_clean)
+			: $this->post($index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the COOKIE array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_COOKIE
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function cookie($index = NULL, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the SERVER array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_SERVER
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function server($index, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the php://input stream
+	 *
+	 * Useful when you need to access PUT, DELETE or PATCH request data.
+	 *
+	 * @param	string	$index		Index for item to be fetched
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function input_stream($index = NULL, $xss_clean = NULL)
+	{
+		// Prior to PHP 5.6, the input stream can only be read once,
+		// so we'll need to check if we have already done that first.
+		if ( ! is_array($this->_input_stream))
+		{
+			// $this->raw_input_stream will trigger __get().
+			parse_str($this->raw_input_stream, $this->_input_stream);
+			is_array($this->_input_stream) OR $this->_input_stream = array();
+		}
+
+		return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Set cookie
+	 *
+	 * Accepts an arbitrary number of parameters (up to 7) or an associative
+	 * array in the first parameter containing all the values.
+	 *
+	 * @param	string|mixed[]	$name		Cookie name or an array containing parameters
+	 * @param	string		$value		Cookie value
+	 * @param	int		$expire		Cookie expiration time in seconds
+	 * @param	string		$domain		Cookie domain (e.g.: '.yourdomain.com')
+	 * @param	string		$path		Cookie path (default: '/')
+	 * @param	string		$prefix		Cookie name prefix
+	 * @param	bool		$secure		Whether to only transfer cookies via SSL
+	 * @param	bool		$httponly	Whether to only makes the cookie accessible via HTTP (no javascript)
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE)
+	{
+		if (is_array($name))
+		{
+			// always leave 'name' in last place, as the loop will break otherwise, due to $$item
+			foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item)
+			{
+				if (isset($name[$item]))
+				{
+					$$item = $name[$item];
+				}
+			}
+		}
+
+		if ($prefix === '' && config_item('cookie_prefix') !== '')
+		{
+			$prefix = config_item('cookie_prefix');
+		}
+
+		if ($domain == '' && config_item('cookie_domain') != '')
+		{
+			$domain = config_item('cookie_domain');
+		}
+
+		if ($path === '/' && config_item('cookie_path') !== '/')
+		{
+			$path = config_item('cookie_path');
+		}
+
+		if ($secure === FALSE && config_item('cookie_secure') === TRUE)
+		{
+			$secure = config_item('cookie_secure');
+		}
+
+		if ($httponly === FALSE && config_item('cookie_httponly') !== FALSE)
+		{
+			$httponly = config_item('cookie_httponly');
+		}
+
+		if ( ! is_numeric($expire))
+		{
+			$expire = time() - 86500;
+		}
+		else
+		{
+			$expire = ($expire > 0) ? time() + $expire : 0;
+		}
+
+//		setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly);
+
+		// Save cookie in Output object
+		// added by ci-phpunit-test
+		$CI =& get_instance();
+		$output = $CI->output;
+		$output->_cookies[$prefix.$name][] = [
+			'value' => $value,
+			'expire' => $expire,
+			'path' => $path,
+			'domain' => $domain,
+			'secure' => $secure,
+			'httponly' => $httponly,
+		];
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch the IP Address
+	 *
+	 * Determines and validates the visitor's IP address.
+	 *
+	 * @return	string	IP address
+	 */
+	public function ip_address()
+	{
+		if ($this->ip_address !== FALSE)
+		{
+			return $this->ip_address;
+		}
+
+		$proxy_ips = config_item('proxy_ips');
+		if ( ! empty($proxy_ips) && ! is_array($proxy_ips))
+		{
+			$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
+		}
+
+		$this->ip_address = $this->server('REMOTE_ADDR');
+
+		if ($proxy_ips)
+		{
+			foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
+			{
+				if (($spoof = $this->server($header)) !== NULL)
+				{
+					// Some proxies typically list the whole chain of IP
+					// addresses through which the client has reached us.
+					// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
+					sscanf($spoof, '%[^,]', $spoof);
+
+					if ( ! $this->valid_ip($spoof))
+					{
+						$spoof = NULL;
+					}
+					else
+					{
+						break;
+					}
+				}
+			}
+
+			if ($spoof)
+			{
+				for ($i = 0, $c = count($proxy_ips); $i < $c; $i++)
+				{
+					// Check if we have an IP address or a subnet
+					if (strpos($proxy_ips[$i], '/') === FALSE)
+					{
+						// An IP address (and not a subnet) is specified.
+						// We can compare right away.
+						if ($proxy_ips[$i] === $this->ip_address)
+						{
+							$this->ip_address = $spoof;
+							break;
+						}
+
+						continue;
+					}
+
+					// We have a subnet ... now the heavy lifting begins
+					isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
+
+					// If the proxy entry doesn't match the IP protocol - skip it
+					if (strpos($proxy_ips[$i], $separator) === FALSE)
+					{
+						continue;
+					}
+
+					// Convert the REMOTE_ADDR IP address to binary, if needed
+					if ( ! isset($ip, $sprintf))
+					{
+						if ($separator === ':')
+						{
+							// Make sure we're have the "full" IPv6 format
+							$ip = explode(':',
+								str_replace('::',
+									str_repeat(':', 9 - substr_count($this->ip_address, ':')),
+									$this->ip_address
+								)
+							);
+
+							for ($j = 0; $j < 8; $j++)
+							{
+								$ip[$j] = intval($ip[$j], 16);
+							}
+
+							$sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
+						}
+						else
+						{
+							$ip = explode('.', $this->ip_address);
+							$sprintf = '%08b%08b%08b%08b';
+						}
+
+						$ip = vsprintf($sprintf, $ip);
+					}
+
+					// Split the netmask length off the network address
+					sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);
+
+					// Again, an IPv6 address is most likely in a compressed form
+					if ($separator === ':')
+					{
+						$netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
+						for ($i = 0; $i < 8; $i++)
+						{
+							$netaddr[$i] = intval($netaddr[$i], 16);
+						}
+					}
+					else
+					{
+						$netaddr = explode('.', $netaddr);
+					}
+
+					// Convert to binary and finally compare
+					if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)
+					{
+						$this->ip_address = $spoof;
+						break;
+					}
+				}
+			}
+		}
+
+		if ( ! $this->valid_ip($this->ip_address))
+		{
+			return $this->ip_address = '0.0.0.0';
+		}
+
+		return $this->ip_address;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Validate IP Address
+	 *
+	 * @param	string	$ip	IP address
+	 * @param	string	$which	IP protocol: 'ipv4' or 'ipv6'
+	 * @return	bool
+	 */
+	public function valid_ip($ip, $which = '')
+	{
+		switch (strtolower($which))
+		{
+			case 'ipv4':
+				$which = FILTER_FLAG_IPV4;
+				break;
+			case 'ipv6':
+				$which = FILTER_FLAG_IPV6;
+				break;
+			default:
+				$which = NULL;
+				break;
+		}
+
+		return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch User Agent string
+	 *
+	 * @return	string|null	User Agent string or NULL if it doesn't exist
+	 */
+	public function user_agent($xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Sanitize Globals
+	 *
+	 * Internal method serving for the following purposes:
+	 *
+	 *	- Unsets $_GET data, if query strings are not enabled
+	 *	- Cleans POST, COOKIE and SERVER data
+	 * 	- Standardizes newline characters to PHP_EOL
+	 *
+	 * @return	void
+	 */
+	protected function _sanitize_globals()
+	{
+		// Is $_GET data allowed? If not we'll set the $_GET to an empty array
+		if ($this->_allow_get_array === FALSE)
+		{
+			$_GET = array();
+		}
+		elseif (is_array($_GET))
+		{
+			foreach ($_GET as $key => $val)
+			{
+				$_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
+			}
+		}
+
+		// Clean $_POST Data
+		if (is_array($_POST))
+		{
+			foreach ($_POST as $key => $val)
+			{
+				$_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
+			}
+		}
+
+		// Clean $_COOKIE Data
+		if (is_array($_COOKIE))
+		{
+			// Also get rid of specially treated cookies that might be set by a server
+			// or silly application, that are of no use to a CI application anyway
+			// but that when present will trip our 'Disallowed Key Characters' alarm
+			// http://www.ietf.org/rfc/rfc2109.txt
+			// note that the key names below are single quoted strings, and are not PHP variables
+			unset(
+				$_COOKIE['$Version'],
+				$_COOKIE['$Path'],
+				$_COOKIE['$Domain']
+			);
+
+			foreach ($_COOKIE as $key => $val)
+			{
+				if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE)
+				{
+					$_COOKIE[$cookie_key] = $this->_clean_input_data($val);
+				}
+				else
+				{
+					unset($_COOKIE[$key]);
+				}
+			}
+		}
+
+		// Sanitize PHP_SELF
+		$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
+
+		log_message('debug', 'Global POST, GET and COOKIE data sanitized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clean Input Data
+	 *
+	 * Internal method that aids in escaping data and
+	 * standardizing newline characters to PHP_EOL.
+	 *
+	 * @param	string|string[]	$str	Input string(s)
+	 * @return	string
+	 */
+	protected function _clean_input_data($str)
+	{
+		if (is_array($str))
+		{
+			$new_array = array();
+			foreach (array_keys($str) as $key)
+			{
+				$new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]);
+			}
+			return $new_array;
+		}
+
+		/* We strip slashes if magic quotes is on to keep things consistent
+
+		   NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
+		         it will probably not exist in future versions at all.
+		*/
+		if ( ! is_php('5.4') && get_magic_quotes_gpc())
+		{
+			$str = stripslashes($str);
+		}
+
+		// Clean UTF-8 if supported
+		if (UTF8_ENABLED === TRUE)
+		{
+			$str = $this->uni->clean_string($str);
+		}
+
+		// Remove control characters
+		$str = remove_invisible_characters($str, FALSE);
+
+		// Standardize newlines if needed
+		if ($this->_standardize_newlines === TRUE)
+		{
+			return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str);
+		}
+
+		return $str;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clean Keys
+	 *
+	 * Internal method that helps to prevent malicious users
+	 * from trying to exploit keys we make sure that keys are
+	 * only named with alpha-numeric text and a few other items.
+	 *
+	 * @param	string	$str	Input string
+	 * @param	bool	$fatal	Whether to terminate script exection
+	 *				or to return FALSE if an invalid
+	 *				key is encountered
+	 * @return	string|bool
+	 */
+	protected function _clean_input_keys($str, $fatal = TRUE)
+	{
+		if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str))
+		{
+			if ($fatal === TRUE)
+			{
+				return FALSE;
+			}
+			else
+			{
+				set_status_header(503);
+				echo 'Disallowed Key Characters.';
+				exit(7); // EXIT_USER_INPUT
+			}
+		}
+
+		// Clean UTF-8 if supported
+		if (UTF8_ENABLED === TRUE)
+		{
+			return $this->uni->clean_string($str);
+		}
+
+		return $str;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Request Headers
+	 *
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	array
+	 */
+	public function request_headers($xss_clean = FALSE)
+	{
+		// If header is already defined, return it immediately
+		if ( ! empty($this->headers))
+		{
+			return $this->headers;
+		}
+
+		// In Apache, you can simply call apache_request_headers()
+		if (function_exists('apache_request_headers'))
+		{
+			return $this->headers = apache_request_headers();
+		}
+
+		$this->headers['Content-Type'] = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');
+
+		foreach ($_SERVER as $key => $val)
+		{
+			if (sscanf($key, 'HTTP_%s', $header) === 1)
+			{
+				// take SOME_HEADER and turn it into Some-Header
+				$header = str_replace('_', ' ', strtolower($header));
+				$header = str_replace(' ', '-', ucwords($header));
+
+				$this->headers[$header] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
+			}
+		}
+
+		return $this->headers;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Request Header
+	 *
+	 * Returns the value of a single member of the headers class member
+	 *
+	 * @param	string		$index		Header name
+	 * @param	bool		$xss_clean	Whether to apply XSS filtering
+	 * @return	string|null	The requested header on success or NULL on failure
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function get_request_header($index, $xss_clean = FALSE)
+	{
+//		static $headers;
+
+		if ( ! isset($headers))
+		{
+			empty($this->headers) && $this->request_headers();
+			foreach ($this->headers as $key => $value)
+			{
+				$headers[strtolower($key)] = $value;
+			}
+		}
+
+		$index = strtolower($index);
+
+		if ( ! isset($headers[$index]))
+		{
+			return NULL;
+		}
+
+		return ($xss_clean === TRUE)
+			? $this->security->xss_clean($headers[$index])
+			: $headers[$index];
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is AJAX request?
+	 *
+	 * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
+	 *
+	 * @return 	bool
+	 */
+	public function is_ajax_request()
+	{
+		return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is CLI request?
+	 *
+	 * Test to see if a request was made from the command line.
+	 *
+	 * @deprecated	3.0.0	Use is_cli() instead
+	 * @return	bool
+	 */
+	public function is_cli_request()
+	{
+		return is_cli();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Request Method
+	 *
+	 * Return the request method
+	 *
+	 * @param	bool	$upper	Whether to return in upper or lower case
+	 *				(default: FALSE)
+	 * @return 	string
+	 */
+	public function method($upper = FALSE)
+	{
+		return ($upper)
+			? strtoupper($this->server('REQUEST_METHOD'))
+			: strtolower($this->server('REQUEST_METHOD'));
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Magic __get()
+	 *
+	 * Allows read access to protected properties
+	 *
+	 * @param	string	$name
+	 * @return	mixed
+	 */
+	public function __get($name)
+	{
+		if ($name === 'raw_input_stream')
+		{
+			isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
+			return $this->_raw_input_stream;
+		}
+		elseif ($name === 'ip_address')
+		{
+			return $this->ip_address;
+		}
+	}
+
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Input.php-3.0.3 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Input.php-3.0.3
new file mode 100644
index 00000000000..e2cd31f68b4
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Input.php-3.0.3
@@ -0,0 +1,910 @@
+_allow_get_array		= (config_item('allow_get_array') === TRUE);
+		$this->_enable_xss		= (config_item('global_xss_filtering') === TRUE);
+		$this->_enable_csrf		= (config_item('csrf_protection') === TRUE);
+		$this->_standardize_newlines	= (bool) config_item('standardize_newlines');
+
+		$this->security =& load_class('Security', 'core');
+
+		// Do we need the UTF-8 class?
+		if (UTF8_ENABLED === TRUE)
+		{
+			$this->uni =& load_class('Utf8', 'core');
+		}
+
+		// Sanitize global arrays
+		$this->_sanitize_globals();
+
+		// CSRF Protection check
+		if ($this->_enable_csrf === TRUE && ! is_cli())
+		{
+			$this->security->csrf_verify();
+		}
+
+		log_message('info', 'Input Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch from array
+	 *
+	 * Internal method used to retrieve values from global arrays.
+	 *
+	 * @param	array	&$array		$_GET, $_POST, $_COOKIE, $_SERVER, etc.
+	 * @param	mixed	$index		Index for item to be fetched from $array
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL)
+	{
+		is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
+		// If $index is NULL, it means that the whole $array is requested
+		isset($index) OR $index = array_keys($array);
+
+		// allow fetching multiple keys at once
+		if (is_array($index))
+		{
+			$output = array();
+			foreach ($index as $key)
+			{
+				$output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);
+			}
+
+			return $output;
+		}
+
+		if (isset($array[$index]))
+		{
+			$value = $array[$index];
+		}
+		elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation
+		{
+			$value = $array;
+			for ($i = 0; $i < $count; $i++)
+			{
+				$key = trim($matches[0][$i], '[]');
+				if ($key === '') // Empty notation will return the value as array
+				{
+					break;
+				}
+
+				if (isset($value[$key]))
+				{
+					$value = $value[$key];
+				}
+				else
+				{
+					return NULL;
+				}
+			}
+		}
+		else
+		{
+			return NULL;
+		}
+
+		return ($xss_clean === TRUE)
+			? $this->security->xss_clean($value)
+			: $value;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the GET array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_GET
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function get($index = NULL, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_GET, $index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the POST array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_POST
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function post($index = NULL, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_POST, $index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from POST data with fallback to GET
+	 *
+	 * @param	string	$index		Index for item to be fetched from $_POST or $_GET
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function post_get($index, $xss_clean = NULL)
+	{
+		return isset($_POST[$index])
+			? $this->post($index, $xss_clean)
+			: $this->get($index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from GET data with fallback to POST
+	 *
+	 * @param	string	$index		Index for item to be fetched from $_GET or $_POST
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function get_post($index, $xss_clean = NULL)
+	{
+		return isset($_GET[$index])
+			? $this->get($index, $xss_clean)
+			: $this->post($index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the COOKIE array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_COOKIE
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function cookie($index = NULL, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the SERVER array
+	 *
+	 * @param	mixed	$index		Index for item to be fetched from $_SERVER
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function server($index, $xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch an item from the php://input stream
+	 *
+	 * Useful when you need to access PUT, DELETE or PATCH request data.
+	 *
+	 * @param	string	$index		Index for item to be fetched
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	mixed
+	 */
+	public function input_stream($index = NULL, $xss_clean = NULL)
+	{
+		// Prior to PHP 5.6, the input stream can only be read once,
+		// so we'll need to check if we have already done that first.
+		if ( ! is_array($this->_input_stream))
+		{
+			// $this->raw_input_stream will trigger __get().
+			parse_str($this->raw_input_stream, $this->_input_stream);
+			is_array($this->_input_stream) OR $this->_input_stream = array();
+		}
+
+		return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Set cookie
+	 *
+	 * Accepts an arbitrary number of parameters (up to 7) or an associative
+	 * array in the first parameter containing all the values.
+	 *
+	 * @param	string|mixed[]	$name		Cookie name or an array containing parameters
+	 * @param	string		$value		Cookie value
+	 * @param	int		$expire		Cookie expiration time in seconds
+	 * @param	string		$domain		Cookie domain (e.g.: '.yourdomain.com')
+	 * @param	string		$path		Cookie path (default: '/')
+	 * @param	string		$prefix		Cookie name prefix
+	 * @param	bool		$secure		Whether to only transfer cookies via SSL
+	 * @param	bool		$httponly	Whether to only makes the cookie accessible via HTTP (no javascript)
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE)
+	{
+		if (is_array($name))
+		{
+			// always leave 'name' in last place, as the loop will break otherwise, due to $$item
+			foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item)
+			{
+				if (isset($name[$item]))
+				{
+					$$item = $name[$item];
+				}
+			}
+		}
+
+		if ($prefix === '' && config_item('cookie_prefix') !== '')
+		{
+			$prefix = config_item('cookie_prefix');
+		}
+
+		if ($domain == '' && config_item('cookie_domain') != '')
+		{
+			$domain = config_item('cookie_domain');
+		}
+
+		if ($path === '/' && config_item('cookie_path') !== '/')
+		{
+			$path = config_item('cookie_path');
+		}
+
+		if ($secure === FALSE && config_item('cookie_secure') === TRUE)
+		{
+			$secure = config_item('cookie_secure');
+		}
+
+		if ($httponly === FALSE && config_item('cookie_httponly') !== FALSE)
+		{
+			$httponly = config_item('cookie_httponly');
+		}
+
+		if ( ! is_numeric($expire))
+		{
+			$expire = time() - 86500;
+		}
+		else
+		{
+			$expire = ($expire > 0) ? time() + $expire : 0;
+		}
+
+//		setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly);
+
+		// Save cookie in Output object
+		// added by ci-phpunit-test
+		$CI =& get_instance();
+		$output = $CI->output;
+		$output->_cookies[$prefix.$name][] = [
+			'value' => $value,
+			'expire' => $expire,
+			'path' => $path,
+			'domain' => $domain,
+			'secure' => $secure,
+			'httponly' => $httponly,
+		];
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch the IP Address
+	 *
+	 * Determines and validates the visitor's IP address.
+	 *
+	 * @return	string	IP address
+	 */
+	public function ip_address()
+	{
+		if ($this->ip_address !== FALSE)
+		{
+			return $this->ip_address;
+		}
+
+		$proxy_ips = config_item('proxy_ips');
+		if ( ! empty($proxy_ips) && ! is_array($proxy_ips))
+		{
+			$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
+		}
+
+		$this->ip_address = $this->server('REMOTE_ADDR');
+
+		if ($proxy_ips)
+		{
+			foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
+			{
+				if (($spoof = $this->server($header)) !== NULL)
+				{
+					// Some proxies typically list the whole chain of IP
+					// addresses through which the client has reached us.
+					// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
+					sscanf($spoof, '%[^,]', $spoof);
+
+					if ( ! $this->valid_ip($spoof))
+					{
+						$spoof = NULL;
+					}
+					else
+					{
+						break;
+					}
+				}
+			}
+
+			if ($spoof)
+			{
+				for ($i = 0, $c = count($proxy_ips); $i < $c; $i++)
+				{
+					// Check if we have an IP address or a subnet
+					if (strpos($proxy_ips[$i], '/') === FALSE)
+					{
+						// An IP address (and not a subnet) is specified.
+						// We can compare right away.
+						if ($proxy_ips[$i] === $this->ip_address)
+						{
+							$this->ip_address = $spoof;
+							break;
+						}
+
+						continue;
+					}
+
+					// We have a subnet ... now the heavy lifting begins
+					isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
+
+					// If the proxy entry doesn't match the IP protocol - skip it
+					if (strpos($proxy_ips[$i], $separator) === FALSE)
+					{
+						continue;
+					}
+
+					// Convert the REMOTE_ADDR IP address to binary, if needed
+					if ( ! isset($ip, $sprintf))
+					{
+						if ($separator === ':')
+						{
+							// Make sure we're have the "full" IPv6 format
+							$ip = explode(':',
+								str_replace('::',
+									str_repeat(':', 9 - substr_count($this->ip_address, ':')),
+									$this->ip_address
+								)
+							);
+
+							for ($j = 0; $j < 8; $j++)
+							{
+								$ip[$j] = intval($ip[$j], 16);
+							}
+
+							$sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
+						}
+						else
+						{
+							$ip = explode('.', $this->ip_address);
+							$sprintf = '%08b%08b%08b%08b';
+						}
+
+						$ip = vsprintf($sprintf, $ip);
+					}
+
+					// Split the netmask length off the network address
+					sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);
+
+					// Again, an IPv6 address is most likely in a compressed form
+					if ($separator === ':')
+					{
+						$netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
+						for ($i = 0; $i < 8; $i++)
+						{
+							$netaddr[$i] = intval($netaddr[$i], 16);
+						}
+					}
+					else
+					{
+						$netaddr = explode('.', $netaddr);
+					}
+
+					// Convert to binary and finally compare
+					if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)
+					{
+						$this->ip_address = $spoof;
+						break;
+					}
+				}
+			}
+		}
+
+		if ( ! $this->valid_ip($this->ip_address))
+		{
+			return $this->ip_address = '0.0.0.0';
+		}
+
+		return $this->ip_address;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Validate IP Address
+	 *
+	 * @param	string	$ip	IP address
+	 * @param	string	$which	IP protocol: 'ipv4' or 'ipv6'
+	 * @return	bool
+	 */
+	public function valid_ip($ip, $which = '')
+	{
+		switch (strtolower($which))
+		{
+			case 'ipv4':
+				$which = FILTER_FLAG_IPV4;
+				break;
+			case 'ipv6':
+				$which = FILTER_FLAG_IPV6;
+				break;
+			default:
+				$which = NULL;
+				break;
+		}
+
+		return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch User Agent string
+	 *
+	 * @return	string|null	User Agent string or NULL if it doesn't exist
+	 */
+	public function user_agent($xss_clean = NULL)
+	{
+		return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Sanitize Globals
+	 *
+	 * Internal method serving for the following purposes:
+	 *
+	 *	- Unsets $_GET data, if query strings are not enabled
+	 *	- Cleans POST, COOKIE and SERVER data
+	 * 	- Standardizes newline characters to PHP_EOL
+	 *
+	 * @return	void
+	 */
+	protected function _sanitize_globals()
+	{
+		// Is $_GET data allowed? If not we'll set the $_GET to an empty array
+		if ($this->_allow_get_array === FALSE)
+		{
+			$_GET = array();
+		}
+		elseif (is_array($_GET))
+		{
+			foreach ($_GET as $key => $val)
+			{
+				$_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
+			}
+		}
+
+		// Clean $_POST Data
+		if (is_array($_POST))
+		{
+			foreach ($_POST as $key => $val)
+			{
+				$_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
+			}
+		}
+
+		// Clean $_COOKIE Data
+		if (is_array($_COOKIE))
+		{
+			// Also get rid of specially treated cookies that might be set by a server
+			// or silly application, that are of no use to a CI application anyway
+			// but that when present will trip our 'Disallowed Key Characters' alarm
+			// http://www.ietf.org/rfc/rfc2109.txt
+			// note that the key names below are single quoted strings, and are not PHP variables
+			unset(
+				$_COOKIE['$Version'],
+				$_COOKIE['$Path'],
+				$_COOKIE['$Domain']
+			);
+
+			foreach ($_COOKIE as $key => $val)
+			{
+				if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE)
+				{
+					$_COOKIE[$cookie_key] = $this->_clean_input_data($val);
+				}
+				else
+				{
+					unset($_COOKIE[$key]);
+				}
+			}
+		}
+
+		// Sanitize PHP_SELF
+		$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
+
+		log_message('debug', 'Global POST, GET and COOKIE data sanitized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clean Input Data
+	 *
+	 * Internal method that aids in escaping data and
+	 * standardizing newline characters to PHP_EOL.
+	 *
+	 * @param	string|string[]	$str	Input string(s)
+	 * @return	string
+	 */
+	protected function _clean_input_data($str)
+	{
+		if (is_array($str))
+		{
+			$new_array = array();
+			foreach (array_keys($str) as $key)
+			{
+				$new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]);
+			}
+			return $new_array;
+		}
+
+		/* We strip slashes if magic quotes is on to keep things consistent
+
+		   NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
+		         it will probably not exist in future versions at all.
+		*/
+		if ( ! is_php('5.4') && get_magic_quotes_gpc())
+		{
+			$str = stripslashes($str);
+		}
+
+		// Clean UTF-8 if supported
+		if (UTF8_ENABLED === TRUE)
+		{
+			$str = $this->uni->clean_string($str);
+		}
+
+		// Remove control characters
+		$str = remove_invisible_characters($str, FALSE);
+
+		// Standardize newlines if needed
+		if ($this->_standardize_newlines === TRUE)
+		{
+			return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str);
+		}
+
+		return $str;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clean Keys
+	 *
+	 * Internal method that helps to prevent malicious users
+	 * from trying to exploit keys we make sure that keys are
+	 * only named with alpha-numeric text and a few other items.
+	 *
+	 * @param	string	$str	Input string
+	 * @param	bool	$fatal	Whether to terminate script exection
+	 *				or to return FALSE if an invalid
+	 *				key is encountered
+	 * @return	string|bool
+	 */
+	protected function _clean_input_keys($str, $fatal = TRUE)
+	{
+		if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str))
+		{
+			if ($fatal === TRUE)
+			{
+				return FALSE;
+			}
+			else
+			{
+				set_status_header(503);
+				echo 'Disallowed Key Characters.';
+				exit(7); // EXIT_USER_INPUT
+			}
+		}
+
+		// Clean UTF-8 if supported
+		if (UTF8_ENABLED === TRUE)
+		{
+			return $this->uni->clean_string($str);
+		}
+
+		return $str;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Request Headers
+	 *
+	 * @param	bool	$xss_clean	Whether to apply XSS filtering
+	 * @return	array
+	 */
+	public function request_headers($xss_clean = FALSE)
+	{
+		// If header is already defined, return it immediately
+		if ( ! empty($this->headers))
+		{
+			return $this->headers;
+		}
+
+		// In Apache, you can simply call apache_request_headers()
+		if (function_exists('apache_request_headers'))
+		{
+			return $this->headers = apache_request_headers();
+		}
+
+		$this->headers['Content-Type'] = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');
+
+		foreach ($_SERVER as $key => $val)
+		{
+			if (sscanf($key, 'HTTP_%s', $header) === 1)
+			{
+				// take SOME_HEADER and turn it into Some-Header
+				$header = str_replace('_', ' ', strtolower($header));
+				$header = str_replace(' ', '-', ucwords($header));
+
+				$this->headers[$header] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
+			}
+		}
+
+		return $this->headers;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Request Header
+	 *
+	 * Returns the value of a single member of the headers class member
+	 *
+	 * @param	string		$index		Header name
+	 * @param	bool		$xss_clean	Whether to apply XSS filtering
+	 * @return	string|null	The requested header on success or NULL on failure
+	 */
+	public function get_request_header($index, $xss_clean = FALSE)
+	{
+		static $headers;
+
+		if ( ! isset($headers))
+		{
+			empty($this->headers) && $this->request_headers();
+			foreach ($this->headers as $key => $value)
+			{
+				$headers[strtolower($key)] = $value;
+			}
+		}
+
+		$index = strtolower($index);
+
+		if ( ! isset($headers[$index]))
+		{
+			return NULL;
+		}
+
+		return ($xss_clean === TRUE)
+			? $this->security->xss_clean($headers[$index])
+			: $headers[$index];
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is AJAX request?
+	 *
+	 * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
+	 *
+	 * @return 	bool
+	 */
+	public function is_ajax_request()
+	{
+		return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is CLI request?
+	 *
+	 * Test to see if a request was made from the command line.
+	 *
+	 * @deprecated	3.0.0	Use is_cli() instead
+	 * @return	bool
+	 */
+	public function is_cli_request()
+	{
+		return is_cli();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Request Method
+	 *
+	 * Return the request method
+	 *
+	 * @param	bool	$upper	Whether to return in upper or lower case
+	 *				(default: FALSE)
+	 * @return 	string
+	 */
+	public function method($upper = FALSE)
+	{
+		return ($upper)
+			? strtoupper($this->server('REQUEST_METHOD'))
+			: strtolower($this->server('REQUEST_METHOD'));
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Magic __get()
+	 *
+	 * Allows read access to protected properties
+	 *
+	 * @param	string	$name
+	 * @return	mixed
+	 */
+	public function __get($name)
+	{
+		if ($name === 'raw_input_stream')
+		{
+			isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
+			return $this->_raw_input_stream;
+		}
+		elseif ($name === 'ip_address')
+		{
+			return $this->ip_address;
+		}
+	}
+
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php
new file mode 100644
index 00000000000..6dc490cdf67
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php
@@ -0,0 +1,1443 @@
+ TRUE);
+
+	/**
+	 * List of paths to load libraries from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_library_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of paths to load models from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_model_paths =	array(APPPATH);
+
+	/**
+	 * List of paths to load helpers from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helper_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of cached variables
+	 *
+	 * @var	array
+	 */
+	protected $_ci_cached_vars =	array();
+
+	/**
+	 * List of loaded classes
+	 *
+	 * @var	array
+	 */
+	protected $_ci_classes =	array();
+
+	/**
+	 * List of loaded models
+	 *
+	 * @var	array
+	 */
+	protected $_ci_models =	array();
+
+	/**
+	 * List of loaded helpers
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helpers =	array();
+
+	/**
+	 * List of class name mappings
+	 *
+	 * @var	array
+	 */
+	protected $_ci_varmap =	array(
+		'unit_test' => 'unit',
+		'user_agent' => 'agent'
+	);
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * Sets component load paths, gets the initial output buffering level.
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		$this->_ci_ob_level = ob_get_level();
+		$this->_ci_classes =& is_loaded();
+
+		log_message('info', 'Loader Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initializer
+	 *
+	 * @todo	Figure out a way to move this to the constructor
+	 *		without breaking *package_path*() methods.
+	 * @uses	CI_Loader::_ci_autoloader()
+	 * @used-by	CI_Controller::__construct()
+	 * @return	void
+	 */
+	public function initialize()
+	{
+		$this->_ci_autoloader();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is Loaded
+	 *
+	 * A utility method to test if a class is in the self::$_ci_classes array.
+	 *
+	 * @used-by	Mainly used by Form Helper function _get_validation_object().
+	 *
+	 * @param 	string		$class	Class name to check for
+	 * @return 	string|bool	Class object name if loaded or FALSE
+	 */
+	public function is_loaded($class)
+	{
+		return array_search(ucfirst($class), $this->_ci_classes, TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Library Loader
+	 *
+	 * Loads and instantiates libraries.
+	 * Designed to be called from application controllers.
+	 *
+	 * @param	string	$library	Library name
+	 * @param	array	$params		Optional parameters to pass to the library class constructor
+	 * @param	string	$object_name	An optional object name to assign to
+	 * @return	object
+	 */
+	public function library($library, $params = NULL, $object_name = NULL)
+	{
+		if (empty($library))
+		{
+			return $this;
+		}
+		elseif (is_array($library))
+		{
+			foreach ($library as $key => $value)
+			{
+				if (is_int($key))
+				{
+					$this->library($value, $params);
+				}
+				else
+				{
+					$this->library($key, $params, $value);
+				}
+			}
+
+			return $this;
+		}
+
+		if ($params !== NULL && ! is_array($params))
+		{
+			$params = NULL;
+		}
+
+		$this->_ci_load_library($library, $params, $object_name);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Model Loader
+	 *
+	 * Loads and instantiates models.
+	 *
+	 * @param	string	$model		Model name
+	 * @param	string	$name		An optional object name to assign to
+	 * @param	bool	$db_conn	An optional database connection configuration to initialize
+	 * @return	object
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function model($model, $name = '', $db_conn = FALSE)
+	{
+		if (empty($model))
+		{
+			return $this;
+		}
+		elseif (is_array($model))
+		{
+			foreach ($model as $key => $value)
+			{
+				is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
+			}
+
+			return $this;
+		}
+
+		$path = '';
+
+		// Is the model in a sub-folder? If so, parse out the filename and path.
+		if (($last_slash = strrpos($model, '/')) !== FALSE)
+		{
+			// The path is in front of the last slash
+			$path = substr($model, 0, ++$last_slash);
+
+			// And the model name behind it
+			$model = substr($model, $last_slash);
+		}
+
+		if (empty($name))
+		{
+			$name = $model;
+		}
+
+		if (in_array($name, $this->_ci_models, TRUE))
+		{
+			return $this;
+		}
+
+		$CI =& get_instance();
+		if (isset($CI->$name))
+		{
+			throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);
+		}
+
+		if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
+		{
+			if ($db_conn === TRUE)
+			{
+				$db_conn = '';
+			}
+
+			$this->database($db_conn, FALSE, TRUE);
+		}
+
+		// Note: All of the code under this condition used to be just:
+		//
+		//       load_class('Model', 'core');
+		//
+		//       However, load_class() instantiates classes
+		//       to cache them for later use and that prevents
+		//       MY_Model from being an abstract class and is
+		//       sub-optimal otherwise anyway.
+//		if ( ! class_exists('CI_Model', FALSE))
+//		{
+			$app_path = APPPATH.'core'.DIRECTORY_SEPARATOR;
+			if (file_exists($app_path.'Model.php'))
+			{
+				require_once($app_path.'Model.php');
+				if ( ! class_exists('CI_Model', FALSE))
+				{
+					throw new RuntimeException($app_path."Model.php exists, but doesn't declare class CI_Model");
+				}
+			}
+			elseif ( ! class_exists('CI_Model', FALSE))
+			{
+				require_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php');
+			}
+
+			$class = config_item('subclass_prefix').'Model';
+			if (file_exists($app_path.$class.'.php'))
+			{
+				require_once($app_path.$class.'.php');
+				if ( ! class_exists($class, FALSE))
+				{
+					throw new RuntimeException($app_path.$class.".php exists, but doesn't declare class ".$class);
+				}
+			}
+//		}
+
+		$model = ucfirst($model);
+		if ( ! class_exists($model, FALSE))
+		{
+			foreach ($this->_ci_model_paths as $mod_path)
+			{
+				if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+				{
+					continue;
+				}
+
+				require_once($mod_path.'models/'.$path.$model.'.php');
+				if ( ! class_exists($model, FALSE))
+				{
+					throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model);
+				}
+
+				break;
+			}
+
+			if ( ! class_exists($model, FALSE))
+			{
+				throw new RuntimeException('Unable to locate the model you have specified: '.$model);
+			}
+		}
+//		elseif ( ! is_subclass_of($model, 'CI_Model'))
+//		{
+//			throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model");
+//		}
+
+		$this->_ci_models[] = $name;
+		$CI->$name = new $model();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Database Loader
+	 *
+	 * @param	mixed	$params		Database configuration options
+	 * @param	bool	$return 	Whether to return the database object
+	 * @param	bool	$query_builder	Whether to enable Query Builder
+	 *					(overrides the configuration setting)
+	 *
+	 * @return	object|bool	Database object if $return is set to TRUE,
+	 *					FALSE on failure, CI_Loader instance in any other case
+	 */
+	public function database($params = '', $return = FALSE, $query_builder = NULL)
+	{
+		// Grab the super object
+		$CI =& get_instance();
+
+		// Do we even need to load the database class?
+		if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
+		{
+			return FALSE;
+		}
+
+		require_once(BASEPATH.'database/DB.php');
+
+		if ($return === TRUE)
+		{
+			return DB($params, $query_builder);
+		}
+
+		// Initialize the db variable. Needed to prevent
+		// reference errors with some configurations
+		$CI->db = '';
+
+		// Load the DB class
+		$CI->db =& DB($params, $query_builder);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Utilities Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Utilities class object or not
+	 * @return	object
+	 */
+	public function dbutil($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_utility.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
+		$class = 'CI_DB_'.$db->dbdriver.'_utility';
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbutil = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Forge Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Forge class object or not
+	 * @return	object
+	 */
+	public function dbforge($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_forge.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
+
+		if ( ! empty($db->subdriver))
+		{
+			$driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
+			if (file_exists($driver_path))
+			{
+				require_once($driver_path);
+				$class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
+			}
+		}
+		else
+		{
+			$class = 'CI_DB_'.$db->dbdriver.'_forge';
+		}
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbforge = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * View Loader
+	 *
+	 * Loads "view" files.
+	 *
+	 * @param	string	$view	View name
+	 * @param	array	$vars	An associative array of data
+	 *				to be extracted for use in the view
+	 * @param	bool	$return	Whether to return the view output
+	 *				or leave it to the Output class
+	 * @return	object|string
+	 */
+	public function view($view, $vars = array(), $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Generic File Loader
+	 *
+	 * @param	string	$path	File path
+	 * @param	bool	$return	Whether to return the file output
+	 * @return	object|string
+	 */
+	public function file($path, $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Variables
+	 *
+	 * Once variables are set they become available within
+	 * the controller class and its "view" files.
+	 *
+	 * @param	array|object|string	$vars
+	 *					An associative array or object containing values
+	 *					to be set, or a value's name if string
+	 * @param 	string	$val	Value to set, only used if $vars is a string
+	 * @return	object
+	 */
+	public function vars($vars, $val = '')
+	{
+		if (is_string($vars))
+		{
+			$vars = array($vars => $val);
+		}
+
+		$vars = $this->_ci_object_to_array($vars);
+
+		if (is_array($vars) && count($vars) > 0)
+		{
+			foreach ($vars as $key => $val)
+			{
+				$this->_ci_cached_vars[$key] = $val;
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clear Cached Variables
+	 *
+	 * Clears the cached variables.
+	 *
+	 * @return	CI_Loader
+	 */
+	public function clear_vars()
+	{
+		$this->_ci_cached_vars = array();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variable
+	 *
+	 * Check if a variable is set and retrieve it.
+	 *
+	 * @param	string	$key	Variable name
+	 * @return	mixed	The variable or NULL if not found
+	 */
+	public function get_var($key)
+	{
+		return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variables
+	 *
+	 * Retrieves all loaded variables.
+	 *
+	 * @return	array
+	 */
+	public function get_vars()
+	{
+		return $this->_ci_cached_vars;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Helper Loader
+	 *
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helper($helpers = array())
+	{
+		foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
+		{
+			if (isset($this->_ci_helpers[$helper]))
+			{
+				continue;
+			}
+
+			// Is this a helper extension request?
+			$ext_helper = config_item('subclass_prefix').$helper;
+			$ext_loaded = FALSE;
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$ext_helper.'.php'))
+				{
+					include_once($path.'helpers/'.$ext_helper.'.php');
+					$ext_loaded = TRUE;
+				}
+			}
+
+			// If we have loaded extensions - check if the base one is here
+			if ($ext_loaded === TRUE)
+			{
+				$base_helper = BASEPATH.'helpers/'.$helper.'.php';
+				if ( ! file_exists($base_helper))
+				{
+					show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+				}
+
+				include_once($base_helper);
+				$this->_ci_helpers[$helper] = TRUE;
+				log_message('info', 'Helper loaded: '.$helper);
+				continue;
+			}
+
+			// No extensions found ... try loading regular helpers and/or overrides
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$helper.'.php'))
+				{
+					include_once($path.'helpers/'.$helper.'.php');
+
+					$this->_ci_helpers[$helper] = TRUE;
+					log_message('info', 'Helper loaded: '.$helper);
+					break;
+				}
+			}
+
+			// unable to load the helper
+			if ( ! isset($this->_ci_helpers[$helper]))
+			{
+				show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load Helpers
+	 *
+	 * An alias for the helper() method in case the developer has
+	 * written the plural form of it.
+	 *
+	 * @uses	CI_Loader::helper()
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helpers($helpers = array())
+	{
+		return $this->helper($helpers);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Language Loader
+	 *
+	 * Loads language files.
+	 *
+	 * @param	string|string[]	$files	List of language file names to load
+	 * @param	string		Language name
+	 * @return	object
+	 */
+	public function language($files, $lang = '')
+	{
+		get_instance()->lang->load($files, $lang);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Config Loader
+	 *
+	 * Loads a config file (an alias for CI_Config::load()).
+	 *
+	 * @uses	CI_Config::load()
+	 * @param	string	$file			Configuration file name
+	 * @param	bool	$use_sections		Whether configuration values should be loaded into their own section
+	 * @param	bool	$fail_gracefully	Whether to just return FALSE or display an error message
+	 * @return	bool	TRUE if the file was loaded correctly or FALSE on failure
+	 */
+	public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
+	{
+		return get_instance()->config->load($file, $use_sections, $fail_gracefully);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Driver Loader
+	 *
+	 * Loads a driver library.
+	 *
+	 * @param	string|string[]	$library	Driver name(s)
+	 * @param	array		$params		Optional parameters to pass to the driver
+	 * @param	string		$object_name	An optional object name to assign to
+	 *
+	 * @return	object|bool	Object or FALSE on failure if $library is a string
+	 *				and $object_name is set. CI_Loader instance otherwise.
+	 */
+	public function driver($library, $params = NULL, $object_name = NULL)
+	{
+		if (is_array($library))
+		{
+			foreach ($library as $key => $value)
+			{
+				if (is_int($key))
+				{
+					$this->driver($value, $params);
+				}
+				else
+				{
+					$this->driver($key, $params, $value);
+				}
+			}
+
+			return $this;
+		}
+		elseif (empty($library))
+		{
+			return FALSE;
+		}
+
+		if ( ! class_exists('CI_Driver_Library', FALSE))
+		{
+			// We aren't instantiating an object here, just making the base class available
+			require BASEPATH.'libraries/Driver.php';
+		}
+
+		// We can save the loader some time since Drivers will *always* be in a subfolder,
+		// and typically identically named to the library
+		if ( ! strpos($library, '/'))
+		{
+			$library = ucfirst($library).'/'.$library;
+		}
+
+		return $this->library($library, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add Package Path
+	 *
+	 * Prepends a parent path to the library, model, helper and config
+	 * path arrays.
+	 *
+	 * @see	CI_Loader::$_ci_library_paths
+	 * @see	CI_Loader::$_ci_model_paths
+	 * @see CI_Loader::$_ci_helper_paths
+	 * @see CI_Config::$_config_paths
+	 *
+	 * @param	string	$path		Path to add
+	 * @param 	bool	$view_cascade	(default: TRUE)
+	 * @return	object
+	 */
+	public function add_package_path($path, $view_cascade = TRUE)
+	{
+		$path = rtrim($path, '/').'/';
+
+		array_unshift($this->_ci_library_paths, $path);
+		array_unshift($this->_ci_model_paths, $path);
+		array_unshift($this->_ci_helper_paths, $path);
+
+		$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;
+
+		// Add config file path
+		$config =& $this->_ci_get_component('config');
+		$config->_config_paths[] = $path;
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Package Paths
+	 *
+	 * Return a list of all package paths.
+	 *
+	 * @param	bool	$include_base	Whether to include BASEPATH (default: FALSE)
+	 * @return	array
+	 */
+	public function get_package_paths($include_base = FALSE)
+	{
+		return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Remove Package Path
+	 *
+	 * Remove a path from the library, model, helper and/or config
+	 * path arrays if it exists. If no path is provided, the most recently
+	 * added path will be removed removed.
+	 *
+	 * @param	string	$path	Path to remove
+	 * @return	object
+	 */
+	public function remove_package_path($path = '')
+	{
+		$config =& $this->_ci_get_component('config');
+
+		if ($path === '')
+		{
+			array_shift($this->_ci_library_paths);
+			array_shift($this->_ci_model_paths);
+			array_shift($this->_ci_helper_paths);
+			array_shift($this->_ci_view_paths);
+			array_pop($config->_config_paths);
+		}
+		else
+		{
+			$path = rtrim($path, '/').'/';
+			foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
+			{
+				if (($key = array_search($path, $this->{$var})) !== FALSE)
+				{
+					unset($this->{$var}[$key]);
+				}
+			}
+
+			if (isset($this->_ci_view_paths[$path.'views/']))
+			{
+				unset($this->_ci_view_paths[$path.'views/']);
+			}
+
+			if (($key = array_search($path, $config->_config_paths)) !== FALSE)
+			{
+				unset($config->_config_paths[$key]);
+			}
+		}
+
+		// make sure the application default paths are still in the array
+		$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
+		$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
+		$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Data Loader
+	 *
+	 * Used to load views and files.
+	 *
+	 * Variables are prefixed with _ci_ to avoid symbol collision with
+	 * variables made available to view files.
+	 *
+	 * @used-by	CI_Loader::view()
+	 * @used-by	CI_Loader::file()
+	 * @param	array	$_ci_data	Data to load
+	 * @return	object
+	 */
+	protected function _ci_load($_ci_data)
+	{
+		// Set the default data variables
+		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
+		{
+			$$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
+		}
+
+		$file_exists = FALSE;
+
+		// Set the path to the requested file
+		if (is_string($_ci_path) && $_ci_path !== '')
+		{
+			$_ci_x = explode('/', $_ci_path);
+			$_ci_file = end($_ci_x);
+		}
+		else
+		{
+			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
+			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
+
+			foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
+			{
+				if (file_exists($_ci_view_file.$_ci_file))
+				{
+					$_ci_path = $_ci_view_file.$_ci_file;
+					$file_exists = TRUE;
+					break;
+				}
+
+				if ( ! $cascade)
+				{
+					break;
+				}
+			}
+		}
+
+		if ( ! $file_exists && ! file_exists($_ci_path))
+		{
+			show_error('Unable to load the requested file: '.$_ci_file);
+		}
+
+		// This allows anything loaded using $this->load (views, files, etc.)
+		// to become accessible from within the Controller and Model functions.
+		$_ci_CI =& get_instance();
+		foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
+		{
+			if ( ! isset($this->$_ci_key))
+			{
+				$this->$_ci_key =& $_ci_CI->$_ci_key;
+			}
+		}
+
+		/*
+		 * Extract and cache variables
+		 *
+		 * You can either set variables using the dedicated $this->load->vars()
+		 * function or via the second parameter of this function. We'll merge
+		 * the two types and cache them so that views that are embedded within
+		 * other views can have access to these variables.
+		 */
+		if (is_array($_ci_vars))
+		{
+			foreach (array_keys($_ci_vars) as $key)
+			{
+				if (strncmp($key, '_ci_', 4) === 0)
+				{
+					unset($_ci_vars[$key]);
+				}
+			}
+
+			$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
+		}
+		extract($this->_ci_cached_vars);
+
+		/*
+		 * Buffer the output
+		 *
+		 * We buffer the output for two reasons:
+		 * 1. Speed. You get a significant speed boost.
+		 * 2. So that the final rendered template can be post-processed by
+		 *	the output class. Why do we need post processing? For one thing,
+		 *	in order to show the elapsed page load time. Unless we can
+		 *	intercept the content right before it's sent to the browser and
+		 *	then stop the timer it won't be accurate.
+		 */
+		ob_start();
+
+		// If the PHP installation does not support short tags we'll
+		// do a little string replacement, changing the short tags
+		// to standard PHP echo statements.
+		if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
+		{
+			echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace(' $this->_ci_ob_level + 1)
+		{
+			ob_end_flush();
+		}
+		else
+		{
+			$_ci_CI->output->append_output(ob_get_contents());
+			@ob_end_clean();
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Loader
+	 *
+	 * @used-by	CI_Loader::library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$class		Class name to load
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
+	{
+		// Get the class name, and while we're at it trim any slashes.
+		// The directory path can be included as part of the class name,
+		// but we don't want a leading slash
+		$class = str_replace('.php', '', trim($class, '/'));
+
+		// Was the path included with the class name?
+		// We look for a slash to determine this
+		if (($last_slash = strrpos($class, '/')) !== FALSE)
+		{
+			// Extract the path
+			$subdir = substr($class, 0, ++$last_slash);
+
+			// Get the filename from the path
+			$class = substr($class, $last_slash);
+		}
+		else
+		{
+			$subdir = '';
+		}
+
+		$class = ucfirst($class);
+
+		// Is this a stock library? There are a few special conditions if so ...
+		if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
+		{
+			return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
+		}
+
+		// Let's search for the requested library file and load it.
+		foreach ($this->_ci_library_paths as $path)
+		{
+			// BASEPATH has already been checked for
+			if ($path === BASEPATH)
+			{
+				continue;
+			}
+
+			$filepath = $path.'libraries/'.$subdir.$class.'.php';
+
+			// Safety: Was the class already loaded by a previous call?
+			if (class_exists($class, FALSE))
+			{
+				// Before we deem this to be a duplicate request, let's see
+				// if a custom object name is being supplied. If so, we'll
+				// return a new instance of the object
+				if ($object_name !== NULL)
+				{
+					$CI =& get_instance();
+					if ( ! isset($CI->$object_name))
+					{
+						return $this->_ci_init_library($class, '', $params, $object_name);
+					}
+				}
+
+//				log_message('debug', $class.' class already loaded. Second attempt ignored.');
+//				return;
+			}
+			// Does the file exist? No? Bummer...
+			if ( ! file_exists($filepath))
+			{
+				continue;
+			}
+
+			include_once($filepath);
+			return $this->_ci_init_library($class, '', $params, $object_name);
+		}
+
+		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
+		if ($subdir === '')
+		{
+			return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
+		}
+
+		// If we got this far we were unable to find the requested class.
+		log_message('error', 'Unable to load the requested class: '.$class);
+		show_error('Unable to load the requested class: '.$class);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Stock Library Loader
+	 *
+	 * @used-by	CI_Loader::_ci_load_library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$library	Library name to load
+	 * @param	string	$file_path	Path to the library filename, relative to libraries/
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
+	{
+		$prefix = 'CI_';
+
+//		if (class_exists($prefix.$library_name, FALSE))
+		{
+			if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
+			{
+				$prefix = config_item('subclass_prefix');
+			}
+
+			// Before we deem this to be a duplicate request, let's see
+			// if a custom object name is being supplied. If so, we'll
+			// return a new instance of the object
+			if ($object_name !== NULL)
+			{
+				$CI =& get_instance();
+				if ( ! isset($CI->$object_name))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+			}
+
+//			log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
+//			return;
+		}
+
+		$paths = $this->_ci_library_paths;
+		array_pop($paths); // BASEPATH
+		array_pop($paths); // APPPATH (needs to be the first path checked)
+		array_unshift($paths, APPPATH);
+
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
+			{
+				// Override
+				include_once($path);
+//				if (class_exists($prefix.$library_name, FALSE))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+//				else
+//				{
+//					log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
+//				}
+			}
+		}
+
+		include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
+
+		// Check for extensions
+		$subclass = config_item('subclass_prefix').$library_name;
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
+			{
+				include_once($path);
+				if (class_exists($subclass, FALSE))
+				{
+					$prefix = config_item('subclass_prefix');
+					break;
+				}
+				else
+				{
+					log_message('debug', $path.' exists, but does not declare '.$subclass);
+				}
+			}
+		}
+
+		return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Instantiator
+	 *
+	 * @used-by	CI_Loader::_ci_load_stock_library()
+	 * @used-by	CI_Loader::_ci_load_library()
+	 *
+	 * @param	string		$class		Class name
+	 * @param	string		$prefix		Class name prefix
+	 * @param	array|null|bool	$config		Optional configuration to pass to the class constructor:
+	 *						FALSE to skip;
+	 *						NULL to search in config paths;
+	 *						array containing configuration data
+	 * @param	string		$object_name	Optional object name to assign to
+	 * @return	void
+	 */
+	protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
+	{
+		// Is there an associated config file for this class? Note: these should always be lowercase
+		if ($config === NULL)
+		{
+			// Fetch the config paths containing any package paths
+			$config_component = $this->_ci_get_component('config');
+
+			if (is_array($config_component->_config_paths))
+			{
+				$found = FALSE;
+				foreach ($config_component->_config_paths as $path)
+				{
+					// We test for both uppercase and lowercase, for servers that
+					// are case-sensitive with regard to file names. Load global first,
+					// override with environment next
+					if (file_exists($path.'config/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					// Break on the first found configuration, thus package
+					// files are not overridden by default paths
+					if ($found === TRUE)
+					{
+						break;
+					}
+				}
+			}
+		}
+
+		$class_name = $prefix.$class;
+
+		// Is the class name valid?
+		if ( ! class_exists($class_name, FALSE))
+		{
+			log_message('error', 'Non-existent class: '.$class_name);
+			show_error('Non-existent class: '.$class_name);
+		}
+
+		// Set the variable name we will assign the class to
+		// Was a custom class name supplied? If so we'll use it
+		if (empty($object_name))
+		{
+			$object_name = strtolower($class);
+			if (isset($this->_ci_varmap[$object_name]))
+			{
+				$object_name = $this->_ci_varmap[$object_name];
+			}
+		}
+
+		// Don't overwrite existing properties
+		$CI =& get_instance();
+		if (isset($CI->$object_name))
+		{
+			if ($CI->$object_name instanceof $class_name)
+			{
+				log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+				return;
+			}
+
+			show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
+		}
+
+		// Save the class name and object name
+		$this->_ci_classes[$object_name] = $class;
+
+		// Instantiate the class
+		$CI->$object_name = isset($config)
+			? new $class_name($config)
+			: new $class_name();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Autoloader
+	 *
+	 * Loads component listed in the config/autoload.php file.
+	 *
+	 * @used-by	CI_Loader::initialize()
+	 * @return	void
+	 */
+	protected function _ci_autoloader()
+	{
+		if (file_exists(APPPATH.'config/autoload.php'))
+		{
+			include(APPPATH.'config/autoload.php');
+		}
+
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+		}
+
+		if ( ! isset($autoload))
+		{
+			return;
+		}
+
+		// Autoload packages
+		if (isset($autoload['packages']))
+		{
+			foreach ($autoload['packages'] as $package_path)
+			{
+				$this->add_package_path($package_path);
+			}
+		}
+
+		// Load any custom config file
+		if (count($autoload['config']) > 0)
+		{
+			foreach ($autoload['config'] as $val)
+			{
+				$this->config($val);
+			}
+		}
+
+		// Autoload helpers and languages
+		foreach (array('helper', 'language') as $type)
+		{
+			if (isset($autoload[$type]) && count($autoload[$type]) > 0)
+			{
+				$this->$type($autoload[$type]);
+			}
+		}
+
+		// Autoload drivers
+		if (isset($autoload['drivers']))
+		{
+			$this->driver($autoload['drivers']);
+		}
+
+		// Load libraries
+		if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
+		{
+			// Load the database driver.
+			if (in_array('database', $autoload['libraries']))
+			{
+				$this->database();
+				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
+			}
+
+			// Load all other libraries
+			$this->library($autoload['libraries']);
+		}
+
+		// Autoload models
+		if (isset($autoload['model']))
+		{
+			$this->model($autoload['model']);
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Object to Array translator
+	 *
+	 * Takes an object as input and converts the class variables to
+	 * an associative array with key/value pairs.
+	 *
+	 * @param	object	$object	Object data to translate
+	 * @return	array
+	 */
+	protected function _ci_object_to_array($object)
+	{
+		return is_object($object) ? get_object_vars($object) : $object;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Component getter
+	 *
+	 * Get a reference to a specific library or model.
+	 *
+	 * @param 	string	$component	Component name
+	 * @return	bool
+	 */
+	protected function &_ci_get_component($component)
+	{
+		$CI =& get_instance();
+		return $CI->$component;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Prep filename
+	 *
+	 * This function prepares filenames of various items to
+	 * make their loading more reliable.
+	 *
+	 * @param	string|string[]	$filename	Filename(s)
+	 * @param 	string		$extension	Filename extension
+	 * @return	array
+	 */
+	protected function _ci_prep_filename($filename, $extension)
+	{
+		if ( ! is_array($filename))
+		{
+			return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension));
+		}
+		else
+		{
+			foreach ($filename as $key => $val)
+			{
+				$filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension);
+			}
+
+			return $filename;
+		}
+	}
+
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.0 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.0
new file mode 100644
index 00000000000..032bfa51991
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.0
@@ -0,0 +1,1390 @@
+ TRUE);
+
+	/**
+	 * List of paths to load libraries from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_library_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of paths to load models from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_model_paths =	array(APPPATH);
+
+	/**
+	 * List of paths to load helpers from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helper_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of cached variables
+	 *
+	 * @var	array
+	 */
+	protected $_ci_cached_vars =	array();
+
+	/**
+	 * List of loaded classes
+	 *
+	 * @var	array
+	 */
+	protected $_ci_classes =	array();
+
+	/**
+	 * List of loaded models
+	 *
+	 * @var	array
+	 */
+	protected $_ci_models =	array();
+
+	/**
+	 * List of loaded helpers
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helpers =	array();
+
+	/**
+	 * List of class name mappings
+	 *
+	 * @var	array
+	 */
+	protected $_ci_varmap =	array(
+		'unit_test' => 'unit',
+		'user_agent' => 'agent'
+	);
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * Sets component load paths, gets the initial output buffering level.
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		$this->_ci_ob_level = ob_get_level();
+		$this->_ci_classes =& is_loaded();
+
+		log_message('info', 'Loader Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initializer
+	 *
+	 * @todo	Figure out a way to move this to the constructor
+	 *		without breaking *package_path*() methods.
+	 * @uses	CI_Loader::_ci_autoloader()
+	 * @used-by	CI_Controller::__construct()
+	 * @return	void
+	 */
+	public function initialize()
+	{
+		$this->_ci_autoloader();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is Loaded
+	 *
+	 * A utility method to test if a class is in the self::$_ci_classes array.
+	 *
+	 * @used-by	Mainly used by Form Helper function _get_validation_object().
+	 *
+	 * @param 	string		$class	Class name to check for
+	 * @return 	string|bool	Class object name if loaded or FALSE
+	 */
+	public function is_loaded($class)
+	{
+		return array_search(ucfirst($class), $this->_ci_classes, TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Library Loader
+	 *
+	 * Loads and instantiates libraries.
+	 * Designed to be called from application controllers.
+	 *
+	 * @param	string	$library	Library name
+	 * @param	array	$params		Optional parameters to pass to the library class constructor
+	 * @param	string	$object_name	An optional object name to assign to
+	 * @return	object
+	 */
+	public function library($library, $params = NULL, $object_name = NULL)
+	{
+		if (empty($library))
+		{
+			return $this;
+		}
+		elseif (is_array($library))
+		{
+			foreach ($library as $key => $value)
+			{
+				if (is_int($key))
+				{
+					$this->library($value, $params);
+				}
+				else
+				{
+					$this->library($key, $params, $value);
+				}
+			}
+
+			return $this;
+		}
+
+		if ($params !== NULL && ! is_array($params))
+		{
+			$params = NULL;
+		}
+
+		$this->_ci_load_library($library, $params, $object_name);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Model Loader
+	 *
+	 * Loads and instantiates models.
+	 *
+	 * @param	string	$model		Model name
+	 * @param	string	$name		An optional object name to assign to
+	 * @param	bool	$db_conn	An optional database connection configuration to initialize
+	 * @return	object
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function model($model, $name = '', $db_conn = FALSE)
+	{
+		if (empty($model))
+		{
+			return $this;
+		}
+		elseif (is_array($model))
+		{
+			foreach ($model as $key => $value)
+			{
+				is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
+			}
+
+			return $this;
+		}
+
+		$path = '';
+
+		// Is the model in a sub-folder? If so, parse out the filename and path.
+		if (($last_slash = strrpos($model, '/')) !== FALSE)
+		{
+			// The path is in front of the last slash
+			$path = substr($model, 0, ++$last_slash);
+
+			// And the model name behind it
+			$model = substr($model, $last_slash);
+		}
+
+		if (empty($name))
+		{
+			$name = $model;
+		}
+
+		if (in_array($name, $this->_ci_models, TRUE))
+		{
+			return $this;
+		}
+
+		$CI =& get_instance();
+		if (isset($CI->$name))
+		{
+			show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
+		}
+
+		if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
+		{
+			if ($db_conn === TRUE)
+			{
+				$db_conn = '';
+			}
+
+			$this->database($db_conn, FALSE, TRUE);
+		}
+
+//		if ( ! class_exists('CI_Model', FALSE))
+//		{
+			load_class('Model', 'core');
+//		}
+
+		$model = ucfirst(strtolower($model));
+
+		foreach ($this->_ci_model_paths as $mod_path)
+		{
+			if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+			{
+				continue;
+			}
+
+			require_once($mod_path.'models/'.$path.$model.'.php');
+
+			$this->_ci_models[] = $name;
+			$CI->$name = new $model();
+			return $this;
+		}
+
+		// couldn't find the model
+		show_error('Unable to locate the model you have specified: '.$model);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Database Loader
+	 *
+	 * @param	mixed	$params		Database configuration options
+	 * @param	bool	$return 	Whether to return the database object
+	 * @param	bool	$query_builder	Whether to enable Query Builder
+	 *					(overrides the configuration setting)
+	 *
+	 * @return	object|bool	Database object if $return is set to TRUE,
+	 *					FALSE on failure, CI_Loader instance in any other case
+	 */
+	public function database($params = '', $return = FALSE, $query_builder = NULL)
+	{
+		// Grab the super object
+		$CI =& get_instance();
+
+		// Do we even need to load the database class?
+		if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
+		{
+			return FALSE;
+		}
+
+		require_once(BASEPATH.'database/DB.php');
+
+		if ($return === TRUE)
+		{
+			return DB($params, $query_builder);
+		}
+
+		// Initialize the db variable. Needed to prevent
+		// reference errors with some configurations
+		$CI->db = '';
+
+		// Load the DB class
+		$CI->db =& DB($params, $query_builder);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Utilities Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Utilities class object or not
+	 * @return	object
+	 */
+	public function dbutil($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_utility.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
+		$class = 'CI_DB_'.$db->dbdriver.'_utility';
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbutil = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Forge Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Forge class object or not
+	 * @return	object
+	 */
+	public function dbforge($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_forge.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
+
+		if ( ! empty($db->subdriver))
+		{
+			$driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
+			if (file_exists($driver_path))
+			{
+				require_once($driver_path);
+				$class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
+			}
+		}
+		else
+		{
+			$class = 'CI_DB_'.$db->dbdriver.'_forge';
+		}
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbforge = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * View Loader
+	 *
+	 * Loads "view" files.
+	 *
+	 * @param	string	$view	View name
+	 * @param	array	$vars	An associative array of data
+	 *				to be extracted for use in the view
+	 * @param	bool	$return	Whether to return the view output
+	 *				or leave it to the Output class
+	 * @return	object|string
+	 */
+	public function view($view, $vars = array(), $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Generic File Loader
+	 *
+	 * @param	string	$path	File path
+	 * @param	bool	$return	Whether to return the file output
+	 * @return	object|string
+	 */
+	public function file($path, $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Variables
+	 *
+	 * Once variables are set they become available within
+	 * the controller class and its "view" files.
+	 *
+	 * @param	array|object|string	$vars
+	 *					An associative array or object containing values
+	 *					to be set, or a value's name if string
+	 * @param 	string	$val	Value to set, only used if $vars is a string
+	 * @return	object
+	 */
+	public function vars($vars, $val = '')
+	{
+		if (is_string($vars))
+		{
+			$vars = array($vars => $val);
+		}
+
+		$vars = $this->_ci_object_to_array($vars);
+
+		if (is_array($vars) && count($vars) > 0)
+		{
+			foreach ($vars as $key => $val)
+			{
+				$this->_ci_cached_vars[$key] = $val;
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clear Cached Variables
+	 *
+	 * Clears the cached variables.
+	 *
+	 * @return  object
+	 */
+	public function clear_vars()
+	{
+		$this->_ci_cached_vars = array();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variable
+	 *
+	 * Check if a variable is set and retrieve it.
+	 *
+	 * @param	string	$key	Variable name
+	 * @return	mixed	The variable or NULL if not found
+	 */
+	public function get_var($key)
+	{
+		return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variables
+	 *
+	 * Retrieves all loaded variables.
+	 *
+	 * @return	array
+	 */
+	public function get_vars()
+	{
+		return $this->_ci_cached_vars;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Helper Loader
+	 *
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helper($helpers = array())
+	{
+		foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
+		{
+			if (isset($this->_ci_helpers[$helper]))
+			{
+				continue;
+			}
+
+			// Is this a helper extension request?
+			$ext_helper = config_item('subclass_prefix').$helper;
+			$ext_loaded = FALSE;
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$ext_helper.'.php'))
+				{
+					include_once($path.'helpers/'.$ext_helper.'.php');
+					$ext_loaded = TRUE;
+				}
+			}
+
+			// If we have loaded extensions - check if the base one is here
+			if ($ext_loaded === TRUE)
+			{
+				$base_helper = BASEPATH.'helpers/'.$helper.'.php';
+				if ( ! file_exists($base_helper))
+				{
+					show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+				}
+
+				include_once($base_helper);
+				$this->_ci_helpers[$helper] = TRUE;
+				log_message('info', 'Helper loaded: '.$helper);
+				continue;
+			}
+
+			// No extensions found ... try loading regular helpers and/or overrides
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$helper.'.php'))
+				{
+					include_once($path.'helpers/'.$helper.'.php');
+
+					$this->_ci_helpers[$helper] = TRUE;
+					log_message('info', 'Helper loaded: '.$helper);
+					break;
+				}
+			}
+
+			// unable to load the helper
+			if ( ! isset($this->_ci_helpers[$helper]))
+			{
+				show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load Helpers
+	 *
+	 * An alias for the helper() method in case the developer has
+	 * written the plural form of it.
+	 *
+	 * @uses	CI_Loader::helper()
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helpers($helpers = array())
+	{
+		return $this->helper($helpers);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Language Loader
+	 *
+	 * Loads language files.
+	 *
+	 * @param	string|string[]	$files	List of language file names to load
+	 * @param	string		Language name
+	 * @return	object
+	 */
+	public function language($files, $lang = '')
+	{
+		get_instance()->lang->load($files, $lang);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Config Loader
+	 *
+	 * Loads a config file (an alias for CI_Config::load()).
+	 *
+	 * @uses	CI_Config::load()
+	 * @param	string	$file			Configuration file name
+	 * @param	bool	$use_sections		Whether configuration values should be loaded into their own section
+	 * @param	bool	$fail_gracefully	Whether to just return FALSE or display an error message
+	 * @return	bool	TRUE if the file was loaded correctly or FALSE on failure
+	 */
+	public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
+	{
+		return get_instance()->config->load($file, $use_sections, $fail_gracefully);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Driver Loader
+	 *
+	 * Loads a driver library.
+	 *
+	 * @param	string|string[]	$library	Driver name(s)
+	 * @param	array		$params		Optional parameters to pass to the driver
+	 * @param	string		$object_name	An optional object name to assign to
+	 *
+	 * @return	object|bool	Object or FALSE on failure if $library is a string
+	 *				and $object_name is set. CI_Loader instance otherwise.
+	 */
+	public function driver($library, $params = NULL, $object_name = NULL)
+	{
+		if (is_array($library))
+		{
+			foreach ($library as $driver)
+			{
+				$this->driver($driver);
+			}
+
+			return $this;
+		}
+		elseif (empty($library))
+		{
+			return FALSE;
+		}
+
+		if ( ! class_exists('CI_Driver_Library', FALSE))
+		{
+			// We aren't instantiating an object here, just making the base class available
+			require BASEPATH.'libraries/Driver.php';
+		}
+
+		// We can save the loader some time since Drivers will *always* be in a subfolder,
+		// and typically identically named to the library
+		if ( ! strpos($library, '/'))
+		{
+			$library = ucfirst($library).'/'.$library;
+		}
+
+		return $this->library($library, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add Package Path
+	 *
+	 * Prepends a parent path to the library, model, helper and config
+	 * path arrays.
+	 *
+	 * @see	CI_Loader::$_ci_library_paths
+	 * @see	CI_Loader::$_ci_model_paths
+	 * @see CI_Loader::$_ci_helper_paths
+	 * @see CI_Config::$_config_paths
+	 *
+	 * @param	string	$path		Path to add
+	 * @param 	bool	$view_cascade	(default: TRUE)
+	 * @return	object
+	 */
+	public function add_package_path($path, $view_cascade = TRUE)
+	{
+		$path = rtrim($path, '/').'/';
+
+		array_unshift($this->_ci_library_paths, $path);
+		array_unshift($this->_ci_model_paths, $path);
+		array_unshift($this->_ci_helper_paths, $path);
+
+		$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;
+
+		// Add config file path
+		$config =& $this->_ci_get_component('config');
+		$config->_config_paths[] = $path;
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Package Paths
+	 *
+	 * Return a list of all package paths.
+	 *
+	 * @param	bool	$include_base	Whether to include BASEPATH (default: FALSE)
+	 * @return	array
+	 */
+	public function get_package_paths($include_base = FALSE)
+	{
+		return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Remove Package Path
+	 *
+	 * Remove a path from the library, model, helper and/or config
+	 * path arrays if it exists. If no path is provided, the most recently
+	 * added path will be removed removed.
+	 *
+	 * @param	string	$path	Path to remove
+	 * @return	object
+	 */
+	public function remove_package_path($path = '')
+	{
+		$config =& $this->_ci_get_component('config');
+
+		if ($path === '')
+		{
+			array_shift($this->_ci_library_paths);
+			array_shift($this->_ci_model_paths);
+			array_shift($this->_ci_helper_paths);
+			array_shift($this->_ci_view_paths);
+			array_pop($config->_config_paths);
+		}
+		else
+		{
+			$path = rtrim($path, '/').'/';
+			foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
+			{
+				if (($key = array_search($path, $this->{$var})) !== FALSE)
+				{
+					unset($this->{$var}[$key]);
+				}
+			}
+
+			if (isset($this->_ci_view_paths[$path.'views/']))
+			{
+				unset($this->_ci_view_paths[$path.'views/']);
+			}
+
+			if (($key = array_search($path, $config->_config_paths)) !== FALSE)
+			{
+				unset($config->_config_paths[$key]);
+			}
+		}
+
+		// make sure the application default paths are still in the array
+		$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
+		$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
+		$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Data Loader
+	 *
+	 * Used to load views and files.
+	 *
+	 * Variables are prefixed with _ci_ to avoid symbol collision with
+	 * variables made available to view files.
+	 *
+	 * @used-by	CI_Loader::view()
+	 * @used-by	CI_Loader::file()
+	 * @param	array	$_ci_data	Data to load
+	 * @return	object
+	 */
+	protected function _ci_load($_ci_data)
+	{
+		// Set the default data variables
+		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
+		{
+			$$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
+		}
+
+		$file_exists = FALSE;
+
+		// Set the path to the requested file
+		if (is_string($_ci_path) && $_ci_path !== '')
+		{
+			$_ci_x = explode('/', $_ci_path);
+			$_ci_file = end($_ci_x);
+		}
+		else
+		{
+			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
+			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
+
+			foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
+			{
+				if (file_exists($_ci_view_file.$_ci_file))
+				{
+					$_ci_path = $_ci_view_file.$_ci_file;
+					$file_exists = TRUE;
+					break;
+				}
+
+				if ( ! $cascade)
+				{
+					break;
+				}
+			}
+		}
+
+		if ( ! $file_exists && ! file_exists($_ci_path))
+		{
+			show_error('Unable to load the requested file: '.$_ci_file);
+		}
+
+		// This allows anything loaded using $this->load (views, files, etc.)
+		// to become accessible from within the Controller and Model functions.
+		$_ci_CI =& get_instance();
+		foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
+		{
+			if ( ! isset($this->$_ci_key))
+			{
+				$this->$_ci_key =& $_ci_CI->$_ci_key;
+			}
+		}
+
+		/*
+		 * Extract and cache variables
+		 *
+		 * You can either set variables using the dedicated $this->load->vars()
+		 * function or via the second parameter of this function. We'll merge
+		 * the two types and cache them so that views that are embedded within
+		 * other views can have access to these variables.
+		 */
+		if (is_array($_ci_vars))
+		{
+			$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
+		}
+		extract($this->_ci_cached_vars);
+
+		/*
+		 * Buffer the output
+		 *
+		 * We buffer the output for two reasons:
+		 * 1. Speed. You get a significant speed boost.
+		 * 2. So that the final rendered template can be post-processed by
+		 *	the output class. Why do we need post processing? For one thing,
+		 *	in order to show the elapsed page load time. Unless we can
+		 *	intercept the content right before it's sent to the browser and
+		 *	then stop the timer it won't be accurate.
+		 */
+		ob_start();
+
+		// If the PHP installation does not support short tags we'll
+		// do a little string replacement, changing the short tags
+		// to standard PHP echo statements.
+		if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE && function_usable('eval'))
+		{
+			echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace(' $this->_ci_ob_level + 1)
+		{
+			ob_end_flush();
+		}
+		else
+		{
+			$_ci_CI->output->append_output(ob_get_contents());
+			@ob_end_clean();
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Loader
+	 *
+	 * @used-by	CI_Loader::library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$class		Class name to load
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
+	{
+		// Get the class name, and while we're at it trim any slashes.
+		// The directory path can be included as part of the class name,
+		// but we don't want a leading slash
+		$class = str_replace('.php', '', trim($class, '/'));
+
+		// Was the path included with the class name?
+		// We look for a slash to determine this
+		if (($last_slash = strrpos($class, '/')) !== FALSE)
+		{
+			// Extract the path
+			$subdir = substr($class, 0, ++$last_slash);
+
+			// Get the filename from the path
+			$class = substr($class, $last_slash);
+		}
+		else
+		{
+			$subdir = '';
+		}
+
+		$class = ucfirst($class);
+
+		// Is this a stock library? There are a few special conditions if so ...
+		if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
+		{
+			return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
+		}
+
+		// Let's search for the requested library file and load it.
+		foreach ($this->_ci_library_paths as $path)
+		{
+			// BASEPATH has already been checked for
+			if ($path === BASEPATH)
+			{
+				continue;
+			}
+
+			$filepath = $path.'libraries/'.$subdir.$class.'.php';
+
+			// Safety: Was the class already loaded by a previous call?
+//			if (class_exists($class, FALSE))
+//			{
+				// Before we deem this to be a duplicate request, let's see
+				// if a custom object name is being supplied. If so, we'll
+				// return a new instance of the object
+				if ($object_name !== NULL)
+				{
+					$CI =& get_instance();
+					if ( ! isset($CI->$object_name))
+					{
+						return $this->_ci_init_library($class, '', $params, $object_name);
+					}
+				}
+
+//				log_message('debug', $class.' class already loaded. Second attempt ignored.');
+//				return;
+//			}
+			// Does the file exist? No? Bummer...
+			if ( ! file_exists($filepath))
+			{
+				continue;
+			}
+
+			include_once($filepath);
+			return $this->_ci_init_library($class, '', $params, $object_name);
+		}
+
+		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
+		if ($subdir === '')
+		{
+			return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
+		}
+
+		// If we got this far we were unable to find the requested class.
+		log_message('error', 'Unable to load the requested class: '.$class);
+		show_error('Unable to load the requested class: '.$class);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Stock Library Loader
+	 *
+	 * @used-by	CI_Loader::_ci_load_library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$library	Library name to load
+	 * @param	string	$file_path	Path to the library filename, relative to libraries/
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
+	{
+		$prefix = 'CI_';
+
+//		if (class_exists($prefix.$library_name, FALSE))
+		{
+			if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
+			{
+				$prefix = config_item('subclass_prefix');
+			}
+
+			// Before we deem this to be a duplicate request, let's see
+			// if a custom object name is being supplied. If so, we'll
+			// return a new instance of the object
+			if ($object_name !== NULL)
+			{
+				$CI =& get_instance();
+				if ( ! isset($CI->$object_name))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+			}
+
+//			log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
+//			return;
+		}
+
+		$paths = $this->_ci_library_paths;
+		array_pop($paths); // BASEPATH
+		array_pop($paths); // APPPATH (needs to be the first path checked)
+		array_unshift($paths, APPPATH);
+
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
+			{
+				// Override
+				include_once($path);
+//				if (class_exists($prefix.$library_name, FALSE))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+//				else
+//				{
+//					log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
+//				}
+			}
+		}
+
+		include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
+
+		// Check for extensions
+		$subclass = config_item('subclass_prefix').$library_name;
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
+			{
+				include_once($path);
+				if (class_exists($subclass, FALSE))
+				{
+					$prefix = config_item('subclass_prefix');
+					break;
+				}
+				else
+				{
+					log_message('debug', APPPATH.'libraries/'.$file_path.$subclass.'.php exists, but does not declare '.$subclass);
+				}
+			}
+		}
+
+		return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Instantiator
+	 *
+	 * @used-by	CI_Loader::_ci_load_stock_library()
+	 * @used-by	CI_Loader::_ci_load_library()
+	 *
+	 * @param	string		$class		Class name
+	 * @param	string		$prefix		Class name prefix
+	 * @param	array|null|bool	$config		Optional configuration to pass to the class constructor:
+	 *						FALSE to skip;
+	 *						NULL to search in config paths;
+	 *						array containing configuration data
+	 * @param	string		$object_name	Optional object name to assign to
+	 * @return	void
+	 */
+	protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
+	{
+		// Is there an associated config file for this class? Note: these should always be lowercase
+		if ($config === NULL)
+		{
+			// Fetch the config paths containing any package paths
+			$config_component = $this->_ci_get_component('config');
+
+			if (is_array($config_component->_config_paths))
+			{
+				$found = FALSE;
+				foreach ($config_component->_config_paths as $path)
+				{
+					// We test for both uppercase and lowercase, for servers that
+					// are case-sensitive with regard to file names. Load global first,
+					// override with environment next
+					if (file_exists($path.'config/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					// Break on the first found configuration, thus package
+					// files are not overridden by default paths
+					if ($found === TRUE)
+					{
+						break;
+					}
+				}
+			}
+		}
+
+		$class_name = $prefix.$class;
+
+		// Is the class name valid?
+		if ( ! class_exists($class_name, FALSE))
+		{
+			log_message('error', 'Non-existent class: '.$class_name);
+			show_error('Non-existent class: '.$class_name);
+		}
+
+		// Set the variable name we will assign the class to
+		// Was a custom class name supplied? If so we'll use it
+		if (empty($object_name))
+		{
+			$object_name = strtolower($class);
+			if (isset($this->_ci_varmap[$object_name]))
+			{
+				$object_name = $this->_ci_varmap[$object_name];
+			}
+		}
+
+		// Don't overwrite existing properties
+		$CI =& get_instance();
+		if (isset($CI->$object_name))
+		{
+			if ($CI->$object_name instanceof $class_name)
+			{
+				log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+				return;
+			}
+
+			show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
+		}
+
+		// Save the class name and object name
+		$this->_ci_classes[$object_name] = $class;
+
+		// Instantiate the class
+		$CI->$object_name = isset($config)
+			? new $class_name($config)
+			: new $class_name();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Autoloader
+	 *
+	 * Loads component listed in the config/autoload.php file.
+	 *
+	 * @used-by	CI_Loader::initialize()
+	 * @return	void
+	 */
+	protected function _ci_autoloader()
+	{
+		if (file_exists(APPPATH.'config/autoload.php'))
+		{
+			include(APPPATH.'config/autoload.php');
+		}
+
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+		}
+
+		if ( ! isset($autoload))
+		{
+			return;
+		}
+
+		// Autoload packages
+		if (isset($autoload['packages']))
+		{
+			foreach ($autoload['packages'] as $package_path)
+			{
+				$this->add_package_path($package_path);
+			}
+		}
+
+		// Load any custom config file
+		if (count($autoload['config']) > 0)
+		{
+			foreach ($autoload['config'] as $val)
+			{
+				$this->config($val);
+			}
+		}
+
+		// Autoload helpers and languages
+		foreach (array('helper', 'language') as $type)
+		{
+			if (isset($autoload[$type]) && count($autoload[$type]) > 0)
+			{
+				$this->$type($autoload[$type]);
+			}
+		}
+
+		// Autoload drivers
+		if (isset($autoload['drivers']))
+		{
+			foreach ($autoload['drivers'] as $item)
+			{
+				$this->driver($item);
+			}
+		}
+
+		// Load libraries
+		if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
+		{
+			// Load the database driver.
+			if (in_array('database', $autoload['libraries']))
+			{
+				$this->database();
+				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
+			}
+
+			// Load all other libraries
+			foreach ($autoload['libraries'] as $item)
+			{
+				$this->library($item);
+			}
+		}
+
+		// Autoload models
+		if (isset($autoload['model']))
+		{
+			$this->model($autoload['model']);
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Object to Array translator
+	 *
+	 * Takes an object as input and converts the class variables to
+	 * an associative array with key/value pairs.
+	 *
+	 * @param	object	$object	Object data to translate
+	 * @return	array
+	 */
+	protected function _ci_object_to_array($object)
+	{
+		return is_object($object) ? get_object_vars($object) : $object;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Component getter
+	 *
+	 * Get a reference to a specific library or model.
+	 *
+	 * @param 	string	$component	Component name
+	 * @return	bool
+	 */
+	protected function &_ci_get_component($component)
+	{
+		$CI =& get_instance();
+		return $CI->$component;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Prep filename
+	 *
+	 * This function prepares filenames of various items to
+	 * make their loading more reliable.
+	 *
+	 * @param	string|string[]	$filename	Filename(s)
+	 * @param 	string		$extension	Filename extension
+	 * @return	array
+	 */
+	protected function _ci_prep_filename($filename, $extension)
+	{
+		if ( ! is_array($filename))
+		{
+			return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension));
+		}
+		else
+		{
+			foreach ($filename as $key => $val)
+			{
+				$filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension);
+			}
+
+			return $filename;
+		}
+	}
+
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.1 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.1
new file mode 100644
index 00000000000..da605e2cfc3
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.1
@@ -0,0 +1,1401 @@
+ TRUE);
+
+	/**
+	 * List of paths to load libraries from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_library_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of paths to load models from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_model_paths =	array(APPPATH);
+
+	/**
+	 * List of paths to load helpers from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helper_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of cached variables
+	 *
+	 * @var	array
+	 */
+	protected $_ci_cached_vars =	array();
+
+	/**
+	 * List of loaded classes
+	 *
+	 * @var	array
+	 */
+	protected $_ci_classes =	array();
+
+	/**
+	 * List of loaded models
+	 *
+	 * @var	array
+	 */
+	protected $_ci_models =	array();
+
+	/**
+	 * List of loaded helpers
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helpers =	array();
+
+	/**
+	 * List of class name mappings
+	 *
+	 * @var	array
+	 */
+	protected $_ci_varmap =	array(
+		'unit_test' => 'unit',
+		'user_agent' => 'agent'
+	);
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * Sets component load paths, gets the initial output buffering level.
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		$this->_ci_ob_level = ob_get_level();
+		$this->_ci_classes =& is_loaded();
+
+		log_message('info', 'Loader Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initializer
+	 *
+	 * @todo	Figure out a way to move this to the constructor
+	 *		without breaking *package_path*() methods.
+	 * @uses	CI_Loader::_ci_autoloader()
+	 * @used-by	CI_Controller::__construct()
+	 * @return	void
+	 */
+	public function initialize()
+	{
+		$this->_ci_autoloader();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is Loaded
+	 *
+	 * A utility method to test if a class is in the self::$_ci_classes array.
+	 *
+	 * @used-by	Mainly used by Form Helper function _get_validation_object().
+	 *
+	 * @param 	string		$class	Class name to check for
+	 * @return 	string|bool	Class object name if loaded or FALSE
+	 */
+	public function is_loaded($class)
+	{
+		return array_search(ucfirst($class), $this->_ci_classes, TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Library Loader
+	 *
+	 * Loads and instantiates libraries.
+	 * Designed to be called from application controllers.
+	 *
+	 * @param	string	$library	Library name
+	 * @param	array	$params		Optional parameters to pass to the library class constructor
+	 * @param	string	$object_name	An optional object name to assign to
+	 * @return	object
+	 */
+	public function library($library, $params = NULL, $object_name = NULL)
+	{
+		if (empty($library))
+		{
+			return $this;
+		}
+		elseif (is_array($library))
+		{
+			foreach ($library as $key => $value)
+			{
+				if (is_int($key))
+				{
+					$this->library($value, $params);
+				}
+				else
+				{
+					$this->library($key, $params, $value);
+				}
+			}
+
+			return $this;
+		}
+
+		if ($params !== NULL && ! is_array($params))
+		{
+			$params = NULL;
+		}
+
+		$this->_ci_load_library($library, $params, $object_name);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Model Loader
+	 *
+	 * Loads and instantiates models.
+	 *
+	 * @param	string	$model		Model name
+	 * @param	string	$name		An optional object name to assign to
+	 * @param	bool	$db_conn	An optional database connection configuration to initialize
+	 * @return	object
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function model($model, $name = '', $db_conn = FALSE)
+	{
+		if (empty($model))
+		{
+			return $this;
+		}
+		elseif (is_array($model))
+		{
+			foreach ($model as $key => $value)
+			{
+				is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
+			}
+
+			return $this;
+		}
+
+		$path = '';
+
+		// Is the model in a sub-folder? If so, parse out the filename and path.
+		if (($last_slash = strrpos($model, '/')) !== FALSE)
+		{
+			// The path is in front of the last slash
+			$path = substr($model, 0, ++$last_slash);
+
+			// And the model name behind it
+			$model = substr($model, $last_slash);
+		}
+
+		if (empty($name))
+		{
+			$name = $model;
+		}
+
+		if (in_array($name, $this->_ci_models, TRUE))
+		{
+			return $this;
+		}
+
+		$CI =& get_instance();
+		if (isset($CI->$name))
+		{
+			throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);
+		}
+
+		if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
+		{
+			if ($db_conn === TRUE)
+			{
+				$db_conn = '';
+			}
+
+			$this->database($db_conn, FALSE, TRUE);
+		}
+
+//		if ( ! class_exists('CI_Model', FALSE))
+//		{
+			load_class('Model', 'core');
+//		}
+
+		$model = ucfirst(strtolower($model));
+		if ( ! class_exists($model))
+		{
+			foreach ($this->_ci_model_paths as $mod_path)
+			{
+				if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+				{
+					continue;
+				}
+
+				require_once($mod_path.'models/'.$path.$model.'.php');
+				if ( ! class_exists($model, FALSE))
+				{
+					throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model);
+				}
+
+				break;
+			}
+
+			if ( ! class_exists($model, FALSE))
+			{
+				throw new RuntimeException('Unable to locate the model you have specified: '.$model);
+			}
+		}
+//		elseif ( ! is_subclass_of($model, 'CI_Model'))
+//		{
+//			throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model");
+//		}
+
+		$this->_ci_models[] = $name;
+		$CI->$name = new $model();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Database Loader
+	 *
+	 * @param	mixed	$params		Database configuration options
+	 * @param	bool	$return 	Whether to return the database object
+	 * @param	bool	$query_builder	Whether to enable Query Builder
+	 *					(overrides the configuration setting)
+	 *
+	 * @return	object|bool	Database object if $return is set to TRUE,
+	 *					FALSE on failure, CI_Loader instance in any other case
+	 */
+	public function database($params = '', $return = FALSE, $query_builder = NULL)
+	{
+		// Grab the super object
+		$CI =& get_instance();
+
+		// Do we even need to load the database class?
+		if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
+		{
+			return FALSE;
+		}
+
+		require_once(BASEPATH.'database/DB.php');
+
+		if ($return === TRUE)
+		{
+			return DB($params, $query_builder);
+		}
+
+		// Initialize the db variable. Needed to prevent
+		// reference errors with some configurations
+		$CI->db = '';
+
+		// Load the DB class
+		$CI->db =& DB($params, $query_builder);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Utilities Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Utilities class object or not
+	 * @return	object
+	 */
+	public function dbutil($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_utility.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
+		$class = 'CI_DB_'.$db->dbdriver.'_utility';
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbutil = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Forge Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Forge class object or not
+	 * @return	object
+	 */
+	public function dbforge($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_forge.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
+
+		if ( ! empty($db->subdriver))
+		{
+			$driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
+			if (file_exists($driver_path))
+			{
+				require_once($driver_path);
+				$class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
+			}
+		}
+		else
+		{
+			$class = 'CI_DB_'.$db->dbdriver.'_forge';
+		}
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbforge = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * View Loader
+	 *
+	 * Loads "view" files.
+	 *
+	 * @param	string	$view	View name
+	 * @param	array	$vars	An associative array of data
+	 *				to be extracted for use in the view
+	 * @param	bool	$return	Whether to return the view output
+	 *				or leave it to the Output class
+	 * @return	object|string
+	 */
+	public function view($view, $vars = array(), $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Generic File Loader
+	 *
+	 * @param	string	$path	File path
+	 * @param	bool	$return	Whether to return the file output
+	 * @return	object|string
+	 */
+	public function file($path, $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Variables
+	 *
+	 * Once variables are set they become available within
+	 * the controller class and its "view" files.
+	 *
+	 * @param	array|object|string	$vars
+	 *					An associative array or object containing values
+	 *					to be set, or a value's name if string
+	 * @param 	string	$val	Value to set, only used if $vars is a string
+	 * @return	object
+	 */
+	public function vars($vars, $val = '')
+	{
+		if (is_string($vars))
+		{
+			$vars = array($vars => $val);
+		}
+
+		$vars = $this->_ci_object_to_array($vars);
+
+		if (is_array($vars) && count($vars) > 0)
+		{
+			foreach ($vars as $key => $val)
+			{
+				$this->_ci_cached_vars[$key] = $val;
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clear Cached Variables
+	 *
+	 * Clears the cached variables.
+	 *
+	 * @return  CI_Loader
+	 */
+	public function clear_vars()
+	{
+		$this->_ci_cached_vars = array();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variable
+	 *
+	 * Check if a variable is set and retrieve it.
+	 *
+	 * @param	string	$key	Variable name
+	 * @return	mixed	The variable or NULL if not found
+	 */
+	public function get_var($key)
+	{
+		return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variables
+	 *
+	 * Retrieves all loaded variables.
+	 *
+	 * @return	array
+	 */
+	public function get_vars()
+	{
+		return $this->_ci_cached_vars;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Helper Loader
+	 *
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helper($helpers = array())
+	{
+		foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
+		{
+			if (isset($this->_ci_helpers[$helper]))
+			{
+				continue;
+			}
+
+			// Is this a helper extension request?
+			$ext_helper = config_item('subclass_prefix').$helper;
+			$ext_loaded = FALSE;
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$ext_helper.'.php'))
+				{
+					include_once($path.'helpers/'.$ext_helper.'.php');
+					$ext_loaded = TRUE;
+				}
+			}
+
+			// If we have loaded extensions - check if the base one is here
+			if ($ext_loaded === TRUE)
+			{
+				$base_helper = BASEPATH.'helpers/'.$helper.'.php';
+				if ( ! file_exists($base_helper))
+				{
+					show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+				}
+
+				include_once($base_helper);
+				$this->_ci_helpers[$helper] = TRUE;
+				log_message('info', 'Helper loaded: '.$helper);
+				continue;
+			}
+
+			// No extensions found ... try loading regular helpers and/or overrides
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$helper.'.php'))
+				{
+					include_once($path.'helpers/'.$helper.'.php');
+
+					$this->_ci_helpers[$helper] = TRUE;
+					log_message('info', 'Helper loaded: '.$helper);
+					break;
+				}
+			}
+
+			// unable to load the helper
+			if ( ! isset($this->_ci_helpers[$helper]))
+			{
+				show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load Helpers
+	 *
+	 * An alias for the helper() method in case the developer has
+	 * written the plural form of it.
+	 *
+	 * @uses	CI_Loader::helper()
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helpers($helpers = array())
+	{
+		return $this->helper($helpers);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Language Loader
+	 *
+	 * Loads language files.
+	 *
+	 * @param	string|string[]	$files	List of language file names to load
+	 * @param	string		Language name
+	 * @return	object
+	 */
+	public function language($files, $lang = '')
+	{
+		get_instance()->lang->load($files, $lang);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Config Loader
+	 *
+	 * Loads a config file (an alias for CI_Config::load()).
+	 *
+	 * @uses	CI_Config::load()
+	 * @param	string	$file			Configuration file name
+	 * @param	bool	$use_sections		Whether configuration values should be loaded into their own section
+	 * @param	bool	$fail_gracefully	Whether to just return FALSE or display an error message
+	 * @return	bool	TRUE if the file was loaded correctly or FALSE on failure
+	 */
+	public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
+	{
+		return get_instance()->config->load($file, $use_sections, $fail_gracefully);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Driver Loader
+	 *
+	 * Loads a driver library.
+	 *
+	 * @param	string|string[]	$library	Driver name(s)
+	 * @param	array		$params		Optional parameters to pass to the driver
+	 * @param	string		$object_name	An optional object name to assign to
+	 *
+	 * @return	object|bool	Object or FALSE on failure if $library is a string
+	 *				and $object_name is set. CI_Loader instance otherwise.
+	 */
+	public function driver($library, $params = NULL, $object_name = NULL)
+	{
+		if (is_array($library))
+		{
+			foreach ($library as $driver)
+			{
+				$this->driver($driver);
+			}
+
+			return $this;
+		}
+		elseif (empty($library))
+		{
+			return FALSE;
+		}
+
+		if ( ! class_exists('CI_Driver_Library', FALSE))
+		{
+			// We aren't instantiating an object here, just making the base class available
+			require BASEPATH.'libraries/Driver.php';
+		}
+
+		// We can save the loader some time since Drivers will *always* be in a subfolder,
+		// and typically identically named to the library
+		if ( ! strpos($library, '/'))
+		{
+			$library = ucfirst($library).'/'.$library;
+		}
+
+		return $this->library($library, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add Package Path
+	 *
+	 * Prepends a parent path to the library, model, helper and config
+	 * path arrays.
+	 *
+	 * @see	CI_Loader::$_ci_library_paths
+	 * @see	CI_Loader::$_ci_model_paths
+	 * @see CI_Loader::$_ci_helper_paths
+	 * @see CI_Config::$_config_paths
+	 *
+	 * @param	string	$path		Path to add
+	 * @param 	bool	$view_cascade	(default: TRUE)
+	 * @return	object
+	 */
+	public function add_package_path($path, $view_cascade = TRUE)
+	{
+		$path = rtrim($path, '/').'/';
+
+		array_unshift($this->_ci_library_paths, $path);
+		array_unshift($this->_ci_model_paths, $path);
+		array_unshift($this->_ci_helper_paths, $path);
+
+		$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;
+
+		// Add config file path
+		$config =& $this->_ci_get_component('config');
+		$config->_config_paths[] = $path;
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Package Paths
+	 *
+	 * Return a list of all package paths.
+	 *
+	 * @param	bool	$include_base	Whether to include BASEPATH (default: FALSE)
+	 * @return	array
+	 */
+	public function get_package_paths($include_base = FALSE)
+	{
+		return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Remove Package Path
+	 *
+	 * Remove a path from the library, model, helper and/or config
+	 * path arrays if it exists. If no path is provided, the most recently
+	 * added path will be removed removed.
+	 *
+	 * @param	string	$path	Path to remove
+	 * @return	object
+	 */
+	public function remove_package_path($path = '')
+	{
+		$config =& $this->_ci_get_component('config');
+
+		if ($path === '')
+		{
+			array_shift($this->_ci_library_paths);
+			array_shift($this->_ci_model_paths);
+			array_shift($this->_ci_helper_paths);
+			array_shift($this->_ci_view_paths);
+			array_pop($config->_config_paths);
+		}
+		else
+		{
+			$path = rtrim($path, '/').'/';
+			foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
+			{
+				if (($key = array_search($path, $this->{$var})) !== FALSE)
+				{
+					unset($this->{$var}[$key]);
+				}
+			}
+
+			if (isset($this->_ci_view_paths[$path.'views/']))
+			{
+				unset($this->_ci_view_paths[$path.'views/']);
+			}
+
+			if (($key = array_search($path, $config->_config_paths)) !== FALSE)
+			{
+				unset($config->_config_paths[$key]);
+			}
+		}
+
+		// make sure the application default paths are still in the array
+		$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
+		$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
+		$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Data Loader
+	 *
+	 * Used to load views and files.
+	 *
+	 * Variables are prefixed with _ci_ to avoid symbol collision with
+	 * variables made available to view files.
+	 *
+	 * @used-by	CI_Loader::view()
+	 * @used-by	CI_Loader::file()
+	 * @param	array	$_ci_data	Data to load
+	 * @return	object
+	 */
+	protected function _ci_load($_ci_data)
+	{
+		// Set the default data variables
+		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
+		{
+			$$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
+		}
+
+		$file_exists = FALSE;
+
+		// Set the path to the requested file
+		if (is_string($_ci_path) && $_ci_path !== '')
+		{
+			$_ci_x = explode('/', $_ci_path);
+			$_ci_file = end($_ci_x);
+		}
+		else
+		{
+			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
+			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
+
+			foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
+			{
+				if (file_exists($_ci_view_file.$_ci_file))
+				{
+					$_ci_path = $_ci_view_file.$_ci_file;
+					$file_exists = TRUE;
+					break;
+				}
+
+				if ( ! $cascade)
+				{
+					break;
+				}
+			}
+		}
+
+		if ( ! $file_exists && ! file_exists($_ci_path))
+		{
+			show_error('Unable to load the requested file: '.$_ci_file);
+		}
+
+		// This allows anything loaded using $this->load (views, files, etc.)
+		// to become accessible from within the Controller and Model functions.
+		$_ci_CI =& get_instance();
+		foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
+		{
+			if ( ! isset($this->$_ci_key))
+			{
+				$this->$_ci_key =& $_ci_CI->$_ci_key;
+			}
+		}
+
+		/*
+		 * Extract and cache variables
+		 *
+		 * You can either set variables using the dedicated $this->load->vars()
+		 * function or via the second parameter of this function. We'll merge
+		 * the two types and cache them so that views that are embedded within
+		 * other views can have access to these variables.
+		 */
+		if (is_array($_ci_vars))
+		{
+			$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
+		}
+		extract($this->_ci_cached_vars);
+
+		/*
+		 * Buffer the output
+		 *
+		 * We buffer the output for two reasons:
+		 * 1. Speed. You get a significant speed boost.
+		 * 2. So that the final rendered template can be post-processed by
+		 *	the output class. Why do we need post processing? For one thing,
+		 *	in order to show the elapsed page load time. Unless we can
+		 *	intercept the content right before it's sent to the browser and
+		 *	then stop the timer it won't be accurate.
+		 */
+		ob_start();
+
+		// If the PHP installation does not support short tags we'll
+		// do a little string replacement, changing the short tags
+		// to standard PHP echo statements.
+		if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
+		{
+			echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace(' $this->_ci_ob_level + 1)
+		{
+			ob_end_flush();
+		}
+		else
+		{
+			$_ci_CI->output->append_output(ob_get_contents());
+			@ob_end_clean();
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Loader
+	 *
+	 * @used-by	CI_Loader::library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$class		Class name to load
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
+	{
+		// Get the class name, and while we're at it trim any slashes.
+		// The directory path can be included as part of the class name,
+		// but we don't want a leading slash
+		$class = str_replace('.php', '', trim($class, '/'));
+
+		// Was the path included with the class name?
+		// We look for a slash to determine this
+		if (($last_slash = strrpos($class, '/')) !== FALSE)
+		{
+			// Extract the path
+			$subdir = substr($class, 0, ++$last_slash);
+
+			// Get the filename from the path
+			$class = substr($class, $last_slash);
+		}
+		else
+		{
+			$subdir = '';
+		}
+
+		$class = ucfirst($class);
+
+		// Is this a stock library? There are a few special conditions if so ...
+		if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
+		{
+			return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
+		}
+
+		// Let's search for the requested library file and load it.
+		foreach ($this->_ci_library_paths as $path)
+		{
+			// BASEPATH has already been checked for
+			if ($path === BASEPATH)
+			{
+				continue;
+			}
+
+			$filepath = $path.'libraries/'.$subdir.$class.'.php';
+
+			// Safety: Was the class already loaded by a previous call?
+//			if (class_exists($class, FALSE))
+//			{
+				// Before we deem this to be a duplicate request, let's see
+				// if a custom object name is being supplied. If so, we'll
+				// return a new instance of the object
+				if ($object_name !== NULL)
+				{
+					$CI =& get_instance();
+					if ( ! isset($CI->$object_name))
+					{
+						return $this->_ci_init_library($class, '', $params, $object_name);
+					}
+				}
+
+//				log_message('debug', $class.' class already loaded. Second attempt ignored.');
+//				return;
+//			}
+			// Does the file exist? No? Bummer...
+			if ( ! file_exists($filepath))
+			{
+				continue;
+			}
+
+			include_once($filepath);
+			return $this->_ci_init_library($class, '', $params, $object_name);
+		}
+
+		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
+		if ($subdir === '')
+		{
+			return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
+		}
+
+		// If we got this far we were unable to find the requested class.
+		log_message('error', 'Unable to load the requested class: '.$class);
+		show_error('Unable to load the requested class: '.$class);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Stock Library Loader
+	 *
+	 * @used-by	CI_Loader::_ci_load_library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$library	Library name to load
+	 * @param	string	$file_path	Path to the library filename, relative to libraries/
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
+	{
+		$prefix = 'CI_';
+
+//		if (class_exists($prefix.$library_name, FALSE))
+		{
+			if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
+			{
+				$prefix = config_item('subclass_prefix');
+			}
+
+			// Before we deem this to be a duplicate request, let's see
+			// if a custom object name is being supplied. If so, we'll
+			// return a new instance of the object
+			if ($object_name !== NULL)
+			{
+				$CI =& get_instance();
+				if ( ! isset($CI->$object_name))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+			}
+
+//			log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
+//			return;
+		}
+
+		$paths = $this->_ci_library_paths;
+		array_pop($paths); // BASEPATH
+		array_pop($paths); // APPPATH (needs to be the first path checked)
+		array_unshift($paths, APPPATH);
+
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
+			{
+				// Override
+				include_once($path);
+//				if (class_exists($prefix.$library_name, FALSE))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+//				else
+//				{
+//					log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
+//				}
+			}
+		}
+
+		include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
+
+		// Check for extensions
+		$subclass = config_item('subclass_prefix').$library_name;
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
+			{
+				include_once($path);
+				if (class_exists($subclass, FALSE))
+				{
+					$prefix = config_item('subclass_prefix');
+					break;
+				}
+				else
+				{
+					log_message('debug', $path.' exists, but does not declare '.$subclass);
+				}
+			}
+		}
+
+		return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Instantiator
+	 *
+	 * @used-by	CI_Loader::_ci_load_stock_library()
+	 * @used-by	CI_Loader::_ci_load_library()
+	 *
+	 * @param	string		$class		Class name
+	 * @param	string		$prefix		Class name prefix
+	 * @param	array|null|bool	$config		Optional configuration to pass to the class constructor:
+	 *						FALSE to skip;
+	 *						NULL to search in config paths;
+	 *						array containing configuration data
+	 * @param	string		$object_name	Optional object name to assign to
+	 * @return	void
+	 */
+	protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
+	{
+		// Is there an associated config file for this class? Note: these should always be lowercase
+		if ($config === NULL)
+		{
+			// Fetch the config paths containing any package paths
+			$config_component = $this->_ci_get_component('config');
+
+			if (is_array($config_component->_config_paths))
+			{
+				$found = FALSE;
+				foreach ($config_component->_config_paths as $path)
+				{
+					// We test for both uppercase and lowercase, for servers that
+					// are case-sensitive with regard to file names. Load global first,
+					// override with environment next
+					if (file_exists($path.'config/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					// Break on the first found configuration, thus package
+					// files are not overridden by default paths
+					if ($found === TRUE)
+					{
+						break;
+					}
+				}
+			}
+		}
+
+		$class_name = $prefix.$class;
+
+		// Is the class name valid?
+		if ( ! class_exists($class_name, FALSE))
+		{
+			log_message('error', 'Non-existent class: '.$class_name);
+			show_error('Non-existent class: '.$class_name);
+		}
+
+		// Set the variable name we will assign the class to
+		// Was a custom class name supplied? If so we'll use it
+		if (empty($object_name))
+		{
+			$object_name = strtolower($class);
+			if (isset($this->_ci_varmap[$object_name]))
+			{
+				$object_name = $this->_ci_varmap[$object_name];
+			}
+		}
+
+		// Don't overwrite existing properties
+		$CI =& get_instance();
+		if (isset($CI->$object_name))
+		{
+			if ($CI->$object_name instanceof $class_name)
+			{
+				log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+				return;
+			}
+
+			show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
+		}
+
+		// Save the class name and object name
+		$this->_ci_classes[$object_name] = $class;
+
+		// Instantiate the class
+		$CI->$object_name = isset($config)
+			? new $class_name($config)
+			: new $class_name();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Autoloader
+	 *
+	 * Loads component listed in the config/autoload.php file.
+	 *
+	 * @used-by	CI_Loader::initialize()
+	 * @return	void
+	 */
+	protected function _ci_autoloader()
+	{
+		if (file_exists(APPPATH.'config/autoload.php'))
+		{
+			include(APPPATH.'config/autoload.php');
+		}
+
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+		}
+
+		if ( ! isset($autoload))
+		{
+			return;
+		}
+
+		// Autoload packages
+		if (isset($autoload['packages']))
+		{
+			foreach ($autoload['packages'] as $package_path)
+			{
+				$this->add_package_path($package_path);
+			}
+		}
+
+		// Load any custom config file
+		if (count($autoload['config']) > 0)
+		{
+			foreach ($autoload['config'] as $val)
+			{
+				$this->config($val);
+			}
+		}
+
+		// Autoload helpers and languages
+		foreach (array('helper', 'language') as $type)
+		{
+			if (isset($autoload[$type]) && count($autoload[$type]) > 0)
+			{
+				$this->$type($autoload[$type]);
+			}
+		}
+
+		// Autoload drivers
+		if (isset($autoload['drivers']))
+		{
+			foreach ($autoload['drivers'] as $item)
+			{
+				$this->driver($item);
+			}
+		}
+
+		// Load libraries
+		if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
+		{
+			// Load the database driver.
+			if (in_array('database', $autoload['libraries']))
+			{
+				$this->database();
+				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
+			}
+
+			// Load all other libraries
+			$this->library($autoload['libraries']);
+		}
+
+		// Autoload models
+		if (isset($autoload['model']))
+		{
+			$this->model($autoload['model']);
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Object to Array translator
+	 *
+	 * Takes an object as input and converts the class variables to
+	 * an associative array with key/value pairs.
+	 *
+	 * @param	object	$object	Object data to translate
+	 * @return	array
+	 */
+	protected function _ci_object_to_array($object)
+	{
+		return is_object($object) ? get_object_vars($object) : $object;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Component getter
+	 *
+	 * Get a reference to a specific library or model.
+	 *
+	 * @param 	string	$component	Component name
+	 * @return	bool
+	 */
+	protected function &_ci_get_component($component)
+	{
+		$CI =& get_instance();
+		return $CI->$component;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Prep filename
+	 *
+	 * This function prepares filenames of various items to
+	 * make their loading more reliable.
+	 *
+	 * @param	string|string[]	$filename	Filename(s)
+	 * @param 	string		$extension	Filename extension
+	 * @return	array
+	 */
+	protected function _ci_prep_filename($filename, $extension)
+	{
+		if ( ! is_array($filename))
+		{
+			return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension));
+		}
+		else
+		{
+			foreach ($filename as $key => $val)
+			{
+				$filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension);
+			}
+
+			return $filename;
+		}
+	}
+
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.3 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.3
new file mode 100644
index 00000000000..3ab33cde0bf
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.3
@@ -0,0 +1,1401 @@
+ TRUE);
+
+	/**
+	 * List of paths to load libraries from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_library_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of paths to load models from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_model_paths =	array(APPPATH);
+
+	/**
+	 * List of paths to load helpers from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helper_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of cached variables
+	 *
+	 * @var	array
+	 */
+	protected $_ci_cached_vars =	array();
+
+	/**
+	 * List of loaded classes
+	 *
+	 * @var	array
+	 */
+	protected $_ci_classes =	array();
+
+	/**
+	 * List of loaded models
+	 *
+	 * @var	array
+	 */
+	protected $_ci_models =	array();
+
+	/**
+	 * List of loaded helpers
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helpers =	array();
+
+	/**
+	 * List of class name mappings
+	 *
+	 * @var	array
+	 */
+	protected $_ci_varmap =	array(
+		'unit_test' => 'unit',
+		'user_agent' => 'agent'
+	);
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * Sets component load paths, gets the initial output buffering level.
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		$this->_ci_ob_level = ob_get_level();
+		$this->_ci_classes =& is_loaded();
+
+		log_message('info', 'Loader Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initializer
+	 *
+	 * @todo	Figure out a way to move this to the constructor
+	 *		without breaking *package_path*() methods.
+	 * @uses	CI_Loader::_ci_autoloader()
+	 * @used-by	CI_Controller::__construct()
+	 * @return	void
+	 */
+	public function initialize()
+	{
+		$this->_ci_autoloader();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is Loaded
+	 *
+	 * A utility method to test if a class is in the self::$_ci_classes array.
+	 *
+	 * @used-by	Mainly used by Form Helper function _get_validation_object().
+	 *
+	 * @param 	string		$class	Class name to check for
+	 * @return 	string|bool	Class object name if loaded or FALSE
+	 */
+	public function is_loaded($class)
+	{
+		return array_search(ucfirst($class), $this->_ci_classes, TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Library Loader
+	 *
+	 * Loads and instantiates libraries.
+	 * Designed to be called from application controllers.
+	 *
+	 * @param	string	$library	Library name
+	 * @param	array	$params		Optional parameters to pass to the library class constructor
+	 * @param	string	$object_name	An optional object name to assign to
+	 * @return	object
+	 */
+	public function library($library, $params = NULL, $object_name = NULL)
+	{
+		if (empty($library))
+		{
+			return $this;
+		}
+		elseif (is_array($library))
+		{
+			foreach ($library as $key => $value)
+			{
+				if (is_int($key))
+				{
+					$this->library($value, $params);
+				}
+				else
+				{
+					$this->library($key, $params, $value);
+				}
+			}
+
+			return $this;
+		}
+
+		if ($params !== NULL && ! is_array($params))
+		{
+			$params = NULL;
+		}
+
+		$this->_ci_load_library($library, $params, $object_name);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Model Loader
+	 *
+	 * Loads and instantiates models.
+	 *
+	 * @param	string	$model		Model name
+	 * @param	string	$name		An optional object name to assign to
+	 * @param	bool	$db_conn	An optional database connection configuration to initialize
+	 * @return	object
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function model($model, $name = '', $db_conn = FALSE)
+	{
+		if (empty($model))
+		{
+			return $this;
+		}
+		elseif (is_array($model))
+		{
+			foreach ($model as $key => $value)
+			{
+				is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
+			}
+
+			return $this;
+		}
+
+		$path = '';
+
+		// Is the model in a sub-folder? If so, parse out the filename and path.
+		if (($last_slash = strrpos($model, '/')) !== FALSE)
+		{
+			// The path is in front of the last slash
+			$path = substr($model, 0, ++$last_slash);
+
+			// And the model name behind it
+			$model = substr($model, $last_slash);
+		}
+
+		if (empty($name))
+		{
+			$name = $model;
+		}
+
+		if (in_array($name, $this->_ci_models, TRUE))
+		{
+			return $this;
+		}
+
+		$CI =& get_instance();
+		if (isset($CI->$name))
+		{
+			throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);
+		}
+
+		if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
+		{
+			if ($db_conn === TRUE)
+			{
+				$db_conn = '';
+			}
+
+			$this->database($db_conn, FALSE, TRUE);
+		}
+
+//		if ( ! class_exists('CI_Model', FALSE))
+//		{
+			load_class('Model', 'core');
+//		}
+
+		$model = ucfirst($model);
+		if ( ! class_exists($model))
+		{
+			foreach ($this->_ci_model_paths as $mod_path)
+			{
+				if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+				{
+					continue;
+				}
+
+				require_once($mod_path.'models/'.$path.$model.'.php');
+				if ( ! class_exists($model, FALSE))
+				{
+					throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model);
+				}
+
+				break;
+			}
+
+			if ( ! class_exists($model, FALSE))
+			{
+				throw new RuntimeException('Unable to locate the model you have specified: '.$model);
+			}
+		}
+//		elseif ( ! is_subclass_of($model, 'CI_Model'))
+//		{
+//			throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model");
+//		}
+
+		$this->_ci_models[] = $name;
+		$CI->$name = new $model();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Database Loader
+	 *
+	 * @param	mixed	$params		Database configuration options
+	 * @param	bool	$return 	Whether to return the database object
+	 * @param	bool	$query_builder	Whether to enable Query Builder
+	 *					(overrides the configuration setting)
+	 *
+	 * @return	object|bool	Database object if $return is set to TRUE,
+	 *					FALSE on failure, CI_Loader instance in any other case
+	 */
+	public function database($params = '', $return = FALSE, $query_builder = NULL)
+	{
+		// Grab the super object
+		$CI =& get_instance();
+
+		// Do we even need to load the database class?
+		if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
+		{
+			return FALSE;
+		}
+
+		require_once(BASEPATH.'database/DB.php');
+
+		if ($return === TRUE)
+		{
+			return DB($params, $query_builder);
+		}
+
+		// Initialize the db variable. Needed to prevent
+		// reference errors with some configurations
+		$CI->db = '';
+
+		// Load the DB class
+		$CI->db =& DB($params, $query_builder);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Utilities Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Utilities class object or not
+	 * @return	object
+	 */
+	public function dbutil($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_utility.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
+		$class = 'CI_DB_'.$db->dbdriver.'_utility';
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbutil = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Forge Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Forge class object or not
+	 * @return	object
+	 */
+	public function dbforge($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_forge.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
+
+		if ( ! empty($db->subdriver))
+		{
+			$driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
+			if (file_exists($driver_path))
+			{
+				require_once($driver_path);
+				$class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
+			}
+		}
+		else
+		{
+			$class = 'CI_DB_'.$db->dbdriver.'_forge';
+		}
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbforge = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * View Loader
+	 *
+	 * Loads "view" files.
+	 *
+	 * @param	string	$view	View name
+	 * @param	array	$vars	An associative array of data
+	 *				to be extracted for use in the view
+	 * @param	bool	$return	Whether to return the view output
+	 *				or leave it to the Output class
+	 * @return	object|string
+	 */
+	public function view($view, $vars = array(), $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Generic File Loader
+	 *
+	 * @param	string	$path	File path
+	 * @param	bool	$return	Whether to return the file output
+	 * @return	object|string
+	 */
+	public function file($path, $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Variables
+	 *
+	 * Once variables are set they become available within
+	 * the controller class and its "view" files.
+	 *
+	 * @param	array|object|string	$vars
+	 *					An associative array or object containing values
+	 *					to be set, or a value's name if string
+	 * @param 	string	$val	Value to set, only used if $vars is a string
+	 * @return	object
+	 */
+	public function vars($vars, $val = '')
+	{
+		if (is_string($vars))
+		{
+			$vars = array($vars => $val);
+		}
+
+		$vars = $this->_ci_object_to_array($vars);
+
+		if (is_array($vars) && count($vars) > 0)
+		{
+			foreach ($vars as $key => $val)
+			{
+				$this->_ci_cached_vars[$key] = $val;
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clear Cached Variables
+	 *
+	 * Clears the cached variables.
+	 *
+	 * @return  CI_Loader
+	 */
+	public function clear_vars()
+	{
+		$this->_ci_cached_vars = array();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variable
+	 *
+	 * Check if a variable is set and retrieve it.
+	 *
+	 * @param	string	$key	Variable name
+	 * @return	mixed	The variable or NULL if not found
+	 */
+	public function get_var($key)
+	{
+		return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variables
+	 *
+	 * Retrieves all loaded variables.
+	 *
+	 * @return	array
+	 */
+	public function get_vars()
+	{
+		return $this->_ci_cached_vars;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Helper Loader
+	 *
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helper($helpers = array())
+	{
+		foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
+		{
+			if (isset($this->_ci_helpers[$helper]))
+			{
+				continue;
+			}
+
+			// Is this a helper extension request?
+			$ext_helper = config_item('subclass_prefix').$helper;
+			$ext_loaded = FALSE;
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$ext_helper.'.php'))
+				{
+					include_once($path.'helpers/'.$ext_helper.'.php');
+					$ext_loaded = TRUE;
+				}
+			}
+
+			// If we have loaded extensions - check if the base one is here
+			if ($ext_loaded === TRUE)
+			{
+				$base_helper = BASEPATH.'helpers/'.$helper.'.php';
+				if ( ! file_exists($base_helper))
+				{
+					show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+				}
+
+				include_once($base_helper);
+				$this->_ci_helpers[$helper] = TRUE;
+				log_message('info', 'Helper loaded: '.$helper);
+				continue;
+			}
+
+			// No extensions found ... try loading regular helpers and/or overrides
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$helper.'.php'))
+				{
+					include_once($path.'helpers/'.$helper.'.php');
+
+					$this->_ci_helpers[$helper] = TRUE;
+					log_message('info', 'Helper loaded: '.$helper);
+					break;
+				}
+			}
+
+			// unable to load the helper
+			if ( ! isset($this->_ci_helpers[$helper]))
+			{
+				show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load Helpers
+	 *
+	 * An alias for the helper() method in case the developer has
+	 * written the plural form of it.
+	 *
+	 * @uses	CI_Loader::helper()
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helpers($helpers = array())
+	{
+		return $this->helper($helpers);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Language Loader
+	 *
+	 * Loads language files.
+	 *
+	 * @param	string|string[]	$files	List of language file names to load
+	 * @param	string		Language name
+	 * @return	object
+	 */
+	public function language($files, $lang = '')
+	{
+		get_instance()->lang->load($files, $lang);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Config Loader
+	 *
+	 * Loads a config file (an alias for CI_Config::load()).
+	 *
+	 * @uses	CI_Config::load()
+	 * @param	string	$file			Configuration file name
+	 * @param	bool	$use_sections		Whether configuration values should be loaded into their own section
+	 * @param	bool	$fail_gracefully	Whether to just return FALSE or display an error message
+	 * @return	bool	TRUE if the file was loaded correctly or FALSE on failure
+	 */
+	public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
+	{
+		return get_instance()->config->load($file, $use_sections, $fail_gracefully);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Driver Loader
+	 *
+	 * Loads a driver library.
+	 *
+	 * @param	string|string[]	$library	Driver name(s)
+	 * @param	array		$params		Optional parameters to pass to the driver
+	 * @param	string		$object_name	An optional object name to assign to
+	 *
+	 * @return	object|bool	Object or FALSE on failure if $library is a string
+	 *				and $object_name is set. CI_Loader instance otherwise.
+	 */
+	public function driver($library, $params = NULL, $object_name = NULL)
+	{
+		if (is_array($library))
+		{
+			foreach ($library as $driver)
+			{
+				$this->driver($driver);
+			}
+
+			return $this;
+		}
+		elseif (empty($library))
+		{
+			return FALSE;
+		}
+
+		if ( ! class_exists('CI_Driver_Library', FALSE))
+		{
+			// We aren't instantiating an object here, just making the base class available
+			require BASEPATH.'libraries/Driver.php';
+		}
+
+		// We can save the loader some time since Drivers will *always* be in a subfolder,
+		// and typically identically named to the library
+		if ( ! strpos($library, '/'))
+		{
+			$library = ucfirst($library).'/'.$library;
+		}
+
+		return $this->library($library, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add Package Path
+	 *
+	 * Prepends a parent path to the library, model, helper and config
+	 * path arrays.
+	 *
+	 * @see	CI_Loader::$_ci_library_paths
+	 * @see	CI_Loader::$_ci_model_paths
+	 * @see CI_Loader::$_ci_helper_paths
+	 * @see CI_Config::$_config_paths
+	 *
+	 * @param	string	$path		Path to add
+	 * @param 	bool	$view_cascade	(default: TRUE)
+	 * @return	object
+	 */
+	public function add_package_path($path, $view_cascade = TRUE)
+	{
+		$path = rtrim($path, '/').'/';
+
+		array_unshift($this->_ci_library_paths, $path);
+		array_unshift($this->_ci_model_paths, $path);
+		array_unshift($this->_ci_helper_paths, $path);
+
+		$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;
+
+		// Add config file path
+		$config =& $this->_ci_get_component('config');
+		$config->_config_paths[] = $path;
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Package Paths
+	 *
+	 * Return a list of all package paths.
+	 *
+	 * @param	bool	$include_base	Whether to include BASEPATH (default: FALSE)
+	 * @return	array
+	 */
+	public function get_package_paths($include_base = FALSE)
+	{
+		return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Remove Package Path
+	 *
+	 * Remove a path from the library, model, helper and/or config
+	 * path arrays if it exists. If no path is provided, the most recently
+	 * added path will be removed removed.
+	 *
+	 * @param	string	$path	Path to remove
+	 * @return	object
+	 */
+	public function remove_package_path($path = '')
+	{
+		$config =& $this->_ci_get_component('config');
+
+		if ($path === '')
+		{
+			array_shift($this->_ci_library_paths);
+			array_shift($this->_ci_model_paths);
+			array_shift($this->_ci_helper_paths);
+			array_shift($this->_ci_view_paths);
+			array_pop($config->_config_paths);
+		}
+		else
+		{
+			$path = rtrim($path, '/').'/';
+			foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
+			{
+				if (($key = array_search($path, $this->{$var})) !== FALSE)
+				{
+					unset($this->{$var}[$key]);
+				}
+			}
+
+			if (isset($this->_ci_view_paths[$path.'views/']))
+			{
+				unset($this->_ci_view_paths[$path.'views/']);
+			}
+
+			if (($key = array_search($path, $config->_config_paths)) !== FALSE)
+			{
+				unset($config->_config_paths[$key]);
+			}
+		}
+
+		// make sure the application default paths are still in the array
+		$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
+		$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
+		$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Data Loader
+	 *
+	 * Used to load views and files.
+	 *
+	 * Variables are prefixed with _ci_ to avoid symbol collision with
+	 * variables made available to view files.
+	 *
+	 * @used-by	CI_Loader::view()
+	 * @used-by	CI_Loader::file()
+	 * @param	array	$_ci_data	Data to load
+	 * @return	object
+	 */
+	protected function _ci_load($_ci_data)
+	{
+		// Set the default data variables
+		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
+		{
+			$$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
+		}
+
+		$file_exists = FALSE;
+
+		// Set the path to the requested file
+		if (is_string($_ci_path) && $_ci_path !== '')
+		{
+			$_ci_x = explode('/', $_ci_path);
+			$_ci_file = end($_ci_x);
+		}
+		else
+		{
+			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
+			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
+
+			foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
+			{
+				if (file_exists($_ci_view_file.$_ci_file))
+				{
+					$_ci_path = $_ci_view_file.$_ci_file;
+					$file_exists = TRUE;
+					break;
+				}
+
+				if ( ! $cascade)
+				{
+					break;
+				}
+			}
+		}
+
+		if ( ! $file_exists && ! file_exists($_ci_path))
+		{
+			show_error('Unable to load the requested file: '.$_ci_file);
+		}
+
+		// This allows anything loaded using $this->load (views, files, etc.)
+		// to become accessible from within the Controller and Model functions.
+		$_ci_CI =& get_instance();
+		foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
+		{
+			if ( ! isset($this->$_ci_key))
+			{
+				$this->$_ci_key =& $_ci_CI->$_ci_key;
+			}
+		}
+
+		/*
+		 * Extract and cache variables
+		 *
+		 * You can either set variables using the dedicated $this->load->vars()
+		 * function or via the second parameter of this function. We'll merge
+		 * the two types and cache them so that views that are embedded within
+		 * other views can have access to these variables.
+		 */
+		if (is_array($_ci_vars))
+		{
+			$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
+		}
+		extract($this->_ci_cached_vars);
+
+		/*
+		 * Buffer the output
+		 *
+		 * We buffer the output for two reasons:
+		 * 1. Speed. You get a significant speed boost.
+		 * 2. So that the final rendered template can be post-processed by
+		 *	the output class. Why do we need post processing? For one thing,
+		 *	in order to show the elapsed page load time. Unless we can
+		 *	intercept the content right before it's sent to the browser and
+		 *	then stop the timer it won't be accurate.
+		 */
+		ob_start();
+
+		// If the PHP installation does not support short tags we'll
+		// do a little string replacement, changing the short tags
+		// to standard PHP echo statements.
+		if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
+		{
+			echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace(' $this->_ci_ob_level + 1)
+		{
+			ob_end_flush();
+		}
+		else
+		{
+			$_ci_CI->output->append_output(ob_get_contents());
+			@ob_end_clean();
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Loader
+	 *
+	 * @used-by	CI_Loader::library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$class		Class name to load
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
+	{
+		// Get the class name, and while we're at it trim any slashes.
+		// The directory path can be included as part of the class name,
+		// but we don't want a leading slash
+		$class = str_replace('.php', '', trim($class, '/'));
+
+		// Was the path included with the class name?
+		// We look for a slash to determine this
+		if (($last_slash = strrpos($class, '/')) !== FALSE)
+		{
+			// Extract the path
+			$subdir = substr($class, 0, ++$last_slash);
+
+			// Get the filename from the path
+			$class = substr($class, $last_slash);
+		}
+		else
+		{
+			$subdir = '';
+		}
+
+		$class = ucfirst($class);
+
+		// Is this a stock library? There are a few special conditions if so ...
+		if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
+		{
+			return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
+		}
+
+		// Let's search for the requested library file and load it.
+		foreach ($this->_ci_library_paths as $path)
+		{
+			// BASEPATH has already been checked for
+			if ($path === BASEPATH)
+			{
+				continue;
+			}
+
+			$filepath = $path.'libraries/'.$subdir.$class.'.php';
+
+			// Safety: Was the class already loaded by a previous call?
+			if (class_exists($class, FALSE))
+			{
+				// Before we deem this to be a duplicate request, let's see
+				// if a custom object name is being supplied. If so, we'll
+				// return a new instance of the object
+				if ($object_name !== NULL)
+				{
+					$CI =& get_instance();
+					if ( ! isset($CI->$object_name))
+					{
+						return $this->_ci_init_library($class, '', $params, $object_name);
+					}
+				}
+
+//				log_message('debug', $class.' class already loaded. Second attempt ignored.');
+//				return;
+			}
+			// Does the file exist? No? Bummer...
+			if ( ! file_exists($filepath))
+			{
+				continue;
+			}
+
+			include_once($filepath);
+			return $this->_ci_init_library($class, '', $params, $object_name);
+		}
+
+		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
+		if ($subdir === '')
+		{
+			return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
+		}
+
+		// If we got this far we were unable to find the requested class.
+		log_message('error', 'Unable to load the requested class: '.$class);
+		show_error('Unable to load the requested class: '.$class);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Stock Library Loader
+	 *
+	 * @used-by	CI_Loader::_ci_load_library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$library	Library name to load
+	 * @param	string	$file_path	Path to the library filename, relative to libraries/
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
+	{
+		$prefix = 'CI_';
+
+//		if (class_exists($prefix.$library_name, FALSE))
+		{
+			if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
+			{
+				$prefix = config_item('subclass_prefix');
+			}
+
+			// Before we deem this to be a duplicate request, let's see
+			// if a custom object name is being supplied. If so, we'll
+			// return a new instance of the object
+			if ($object_name !== NULL)
+			{
+				$CI =& get_instance();
+				if ( ! isset($CI->$object_name))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+			}
+
+//			log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
+//			return;
+		}
+
+		$paths = $this->_ci_library_paths;
+		array_pop($paths); // BASEPATH
+		array_pop($paths); // APPPATH (needs to be the first path checked)
+		array_unshift($paths, APPPATH);
+
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
+			{
+				// Override
+				include_once($path);
+//				if (class_exists($prefix.$library_name, FALSE))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+//				else
+//				{
+//					log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
+//				}
+			}
+		}
+
+		include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
+
+		// Check for extensions
+		$subclass = config_item('subclass_prefix').$library_name;
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
+			{
+				include_once($path);
+				if (class_exists($subclass, FALSE))
+				{
+					$prefix = config_item('subclass_prefix');
+					break;
+				}
+				else
+				{
+					log_message('debug', $path.' exists, but does not declare '.$subclass);
+				}
+			}
+		}
+
+		return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Instantiator
+	 *
+	 * @used-by	CI_Loader::_ci_load_stock_library()
+	 * @used-by	CI_Loader::_ci_load_library()
+	 *
+	 * @param	string		$class		Class name
+	 * @param	string		$prefix		Class name prefix
+	 * @param	array|null|bool	$config		Optional configuration to pass to the class constructor:
+	 *						FALSE to skip;
+	 *						NULL to search in config paths;
+	 *						array containing configuration data
+	 * @param	string		$object_name	Optional object name to assign to
+	 * @return	void
+	 */
+	protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
+	{
+		// Is there an associated config file for this class? Note: these should always be lowercase
+		if ($config === NULL)
+		{
+			// Fetch the config paths containing any package paths
+			$config_component = $this->_ci_get_component('config');
+
+			if (is_array($config_component->_config_paths))
+			{
+				$found = FALSE;
+				foreach ($config_component->_config_paths as $path)
+				{
+					// We test for both uppercase and lowercase, for servers that
+					// are case-sensitive with regard to file names. Load global first,
+					// override with environment next
+					if (file_exists($path.'config/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					// Break on the first found configuration, thus package
+					// files are not overridden by default paths
+					if ($found === TRUE)
+					{
+						break;
+					}
+				}
+			}
+		}
+
+		$class_name = $prefix.$class;
+
+		// Is the class name valid?
+		if ( ! class_exists($class_name, FALSE))
+		{
+			log_message('error', 'Non-existent class: '.$class_name);
+			show_error('Non-existent class: '.$class_name);
+		}
+
+		// Set the variable name we will assign the class to
+		// Was a custom class name supplied? If so we'll use it
+		if (empty($object_name))
+		{
+			$object_name = strtolower($class);
+			if (isset($this->_ci_varmap[$object_name]))
+			{
+				$object_name = $this->_ci_varmap[$object_name];
+			}
+		}
+
+		// Don't overwrite existing properties
+		$CI =& get_instance();
+		if (isset($CI->$object_name))
+		{
+			if ($CI->$object_name instanceof $class_name)
+			{
+				log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+				return;
+			}
+
+			show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
+		}
+
+		// Save the class name and object name
+		$this->_ci_classes[$object_name] = $class;
+
+		// Instantiate the class
+		$CI->$object_name = isset($config)
+			? new $class_name($config)
+			: new $class_name();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Autoloader
+	 *
+	 * Loads component listed in the config/autoload.php file.
+	 *
+	 * @used-by	CI_Loader::initialize()
+	 * @return	void
+	 */
+	protected function _ci_autoloader()
+	{
+		if (file_exists(APPPATH.'config/autoload.php'))
+		{
+			include(APPPATH.'config/autoload.php');
+		}
+
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+		}
+
+		if ( ! isset($autoload))
+		{
+			return;
+		}
+
+		// Autoload packages
+		if (isset($autoload['packages']))
+		{
+			foreach ($autoload['packages'] as $package_path)
+			{
+				$this->add_package_path($package_path);
+			}
+		}
+
+		// Load any custom config file
+		if (count($autoload['config']) > 0)
+		{
+			foreach ($autoload['config'] as $val)
+			{
+				$this->config($val);
+			}
+		}
+
+		// Autoload helpers and languages
+		foreach (array('helper', 'language') as $type)
+		{
+			if (isset($autoload[$type]) && count($autoload[$type]) > 0)
+			{
+				$this->$type($autoload[$type]);
+			}
+		}
+
+		// Autoload drivers
+		if (isset($autoload['drivers']))
+		{
+			foreach ($autoload['drivers'] as $item)
+			{
+				$this->driver($item);
+			}
+		}
+
+		// Load libraries
+		if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
+		{
+			// Load the database driver.
+			if (in_array('database', $autoload['libraries']))
+			{
+				$this->database();
+				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
+			}
+
+			// Load all other libraries
+			$this->library($autoload['libraries']);
+		}
+
+		// Autoload models
+		if (isset($autoload['model']))
+		{
+			$this->model($autoload['model']);
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Object to Array translator
+	 *
+	 * Takes an object as input and converts the class variables to
+	 * an associative array with key/value pairs.
+	 *
+	 * @param	object	$object	Object data to translate
+	 * @return	array
+	 */
+	protected function _ci_object_to_array($object)
+	{
+		return is_object($object) ? get_object_vars($object) : $object;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Component getter
+	 *
+	 * Get a reference to a specific library or model.
+	 *
+	 * @param 	string	$component	Component name
+	 * @return	bool
+	 */
+	protected function &_ci_get_component($component)
+	{
+		$CI =& get_instance();
+		return $CI->$component;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Prep filename
+	 *
+	 * This function prepares filenames of various items to
+	 * make their loading more reliable.
+	 *
+	 * @param	string|string[]	$filename	Filename(s)
+	 * @param 	string		$extension	Filename extension
+	 * @return	array
+	 */
+	protected function _ci_prep_filename($filename, $extension)
+	{
+		if ( ! is_array($filename))
+		{
+			return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension));
+		}
+		else
+		{
+			foreach ($filename as $key => $val)
+			{
+				$filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension);
+			}
+
+			return $filename;
+		}
+	}
+
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.4 b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.4
new file mode 100644
index 00000000000..d2d66dffa74
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/core/Loader.php-3.0.4
@@ -0,0 +1,1431 @@
+ TRUE);
+
+	/**
+	 * List of paths to load libraries from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_library_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of paths to load models from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_model_paths =	array(APPPATH);
+
+	/**
+	 * List of paths to load helpers from
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helper_paths =	array(APPPATH, BASEPATH);
+
+	/**
+	 * List of cached variables
+	 *
+	 * @var	array
+	 */
+	protected $_ci_cached_vars =	array();
+
+	/**
+	 * List of loaded classes
+	 *
+	 * @var	array
+	 */
+	protected $_ci_classes =	array();
+
+	/**
+	 * List of loaded models
+	 *
+	 * @var	array
+	 */
+	protected $_ci_models =	array();
+
+	/**
+	 * List of loaded helpers
+	 *
+	 * @var	array
+	 */
+	protected $_ci_helpers =	array();
+
+	/**
+	 * List of class name mappings
+	 *
+	 * @var	array
+	 */
+	protected $_ci_varmap =	array(
+		'unit_test' => 'unit',
+		'user_agent' => 'agent'
+	);
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * Sets component load paths, gets the initial output buffering level.
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		$this->_ci_ob_level = ob_get_level();
+		$this->_ci_classes =& is_loaded();
+
+		log_message('info', 'Loader Class Initialized');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initializer
+	 *
+	 * @todo	Figure out a way to move this to the constructor
+	 *		without breaking *package_path*() methods.
+	 * @uses	CI_Loader::_ci_autoloader()
+	 * @used-by	CI_Controller::__construct()
+	 * @return	void
+	 */
+	public function initialize()
+	{
+		$this->_ci_autoloader();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is Loaded
+	 *
+	 * A utility method to test if a class is in the self::$_ci_classes array.
+	 *
+	 * @used-by	Mainly used by Form Helper function _get_validation_object().
+	 *
+	 * @param 	string		$class	Class name to check for
+	 * @return 	string|bool	Class object name if loaded or FALSE
+	 */
+	public function is_loaded($class)
+	{
+		return array_search(ucfirst($class), $this->_ci_classes, TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Library Loader
+	 *
+	 * Loads and instantiates libraries.
+	 * Designed to be called from application controllers.
+	 *
+	 * @param	string	$library	Library name
+	 * @param	array	$params		Optional parameters to pass to the library class constructor
+	 * @param	string	$object_name	An optional object name to assign to
+	 * @return	object
+	 */
+	public function library($library, $params = NULL, $object_name = NULL)
+	{
+		if (empty($library))
+		{
+			return $this;
+		}
+		elseif (is_array($library))
+		{
+			foreach ($library as $key => $value)
+			{
+				if (is_int($key))
+				{
+					$this->library($value, $params);
+				}
+				else
+				{
+					$this->library($key, $params, $value);
+				}
+			}
+
+			return $this;
+		}
+
+		if ($params !== NULL && ! is_array($params))
+		{
+			$params = NULL;
+		}
+
+		$this->_ci_load_library($library, $params, $object_name);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Model Loader
+	 *
+	 * Loads and instantiates models.
+	 *
+	 * @param	string	$model		Model name
+	 * @param	string	$name		An optional object name to assign to
+	 * @param	bool	$db_conn	An optional database connection configuration to initialize
+	 * @return	object
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	public function model($model, $name = '', $db_conn = FALSE)
+	{
+		if (empty($model))
+		{
+			return $this;
+		}
+		elseif (is_array($model))
+		{
+			foreach ($model as $key => $value)
+			{
+				is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
+			}
+
+			return $this;
+		}
+
+		$path = '';
+
+		// Is the model in a sub-folder? If so, parse out the filename and path.
+		if (($last_slash = strrpos($model, '/')) !== FALSE)
+		{
+			// The path is in front of the last slash
+			$path = substr($model, 0, ++$last_slash);
+
+			// And the model name behind it
+			$model = substr($model, $last_slash);
+		}
+
+		if (empty($name))
+		{
+			$name = $model;
+		}
+
+		if (in_array($name, $this->_ci_models, TRUE))
+		{
+			return $this;
+		}
+
+		$CI =& get_instance();
+		if (isset($CI->$name))
+		{
+			throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);
+		}
+
+		if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
+		{
+			if ($db_conn === TRUE)
+			{
+				$db_conn = '';
+			}
+
+			$this->database($db_conn, FALSE, TRUE);
+		}
+
+		// Note: All of the code under this condition used to be just:
+		//
+		//       load_class('Model', 'core');
+		//
+		//       However, load_class() instantiates classes
+		//       to cache them for later use and that prevents
+		//       MY_Model from being an abstract class and is
+		//       sub-optimal otherwise anyway.
+//		if ( ! class_exists('CI_Model', FALSE))
+//		{
+			$app_path = APPPATH.'core'.DIRECTORY_SEPARATOR;
+			if (file_exists($app_path.'Model.php'))
+			{
+				require_once($app_path.'Model.php');
+				if ( ! class_exists('CI_Model', FALSE))
+				{
+					throw new RuntimeException($app_path."Model.php exists, but doesn't declare class CI_Model");
+				}
+			}
+			elseif ( ! class_exists('CI_Model', FALSE))
+			{
+				require_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php');
+			}
+
+			$class = config_item('subclass_prefix').'Model';
+			if (file_exists($app_path.$class.'.php'))
+			{
+				require_once($app_path.$class.'.php');
+				if ( ! class_exists($class, FALSE))
+				{
+					throw new RuntimeException($app_path.$class.".php exists, but doesn't declare class ".$class);
+				}
+			}
+//		}
+
+		$model = ucfirst($model);
+		if ( ! class_exists($model))
+		{
+			foreach ($this->_ci_model_paths as $mod_path)
+			{
+				if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+				{
+					continue;
+				}
+
+				require_once($mod_path.'models/'.$path.$model.'.php');
+				if ( ! class_exists($model, FALSE))
+				{
+					throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model);
+				}
+
+				break;
+			}
+
+			if ( ! class_exists($model, FALSE))
+			{
+				throw new RuntimeException('Unable to locate the model you have specified: '.$model);
+			}
+		}
+//		elseif ( ! is_subclass_of($model, 'CI_Model'))
+//		{
+//			throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model");
+//		}
+
+		$this->_ci_models[] = $name;
+		$CI->$name = new $model();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Database Loader
+	 *
+	 * @param	mixed	$params		Database configuration options
+	 * @param	bool	$return 	Whether to return the database object
+	 * @param	bool	$query_builder	Whether to enable Query Builder
+	 *					(overrides the configuration setting)
+	 *
+	 * @return	object|bool	Database object if $return is set to TRUE,
+	 *					FALSE on failure, CI_Loader instance in any other case
+	 */
+	public function database($params = '', $return = FALSE, $query_builder = NULL)
+	{
+		// Grab the super object
+		$CI =& get_instance();
+
+		// Do we even need to load the database class?
+		if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
+		{
+			return FALSE;
+		}
+
+		require_once(BASEPATH.'database/DB.php');
+
+		if ($return === TRUE)
+		{
+			return DB($params, $query_builder);
+		}
+
+		// Initialize the db variable. Needed to prevent
+		// reference errors with some configurations
+		$CI->db = '';
+
+		// Load the DB class
+		$CI->db =& DB($params, $query_builder);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Utilities Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Utilities class object or not
+	 * @return	object
+	 */
+	public function dbutil($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_utility.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
+		$class = 'CI_DB_'.$db->dbdriver.'_utility';
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbutil = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load the Database Forge Class
+	 *
+	 * @param	object	$db	Database object
+	 * @param	bool	$return	Whether to return the DB Forge class object or not
+	 * @return	object
+	 */
+	public function dbforge($db = NULL, $return = FALSE)
+	{
+		$CI =& get_instance();
+		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+		{
+			class_exists('CI_DB', FALSE) OR $this->database();
+			$db =& $CI->db;
+		}
+
+		require_once(BASEPATH.'database/DB_forge.php');
+		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
+
+		if ( ! empty($db->subdriver))
+		{
+			$driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
+			if (file_exists($driver_path))
+			{
+				require_once($driver_path);
+				$class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
+			}
+		}
+		else
+		{
+			$class = 'CI_DB_'.$db->dbdriver.'_forge';
+		}
+
+		if ($return === TRUE)
+		{
+			return new $class($db);
+		}
+
+		$CI->dbforge = new $class($db);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * View Loader
+	 *
+	 * Loads "view" files.
+	 *
+	 * @param	string	$view	View name
+	 * @param	array	$vars	An associative array of data
+	 *				to be extracted for use in the view
+	 * @param	bool	$return	Whether to return the view output
+	 *				or leave it to the Output class
+	 * @return	object|string
+	 */
+	public function view($view, $vars = array(), $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Generic File Loader
+	 *
+	 * @param	string	$path	File path
+	 * @param	bool	$return	Whether to return the file output
+	 * @return	object|string
+	 */
+	public function file($path, $return = FALSE)
+	{
+		return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Variables
+	 *
+	 * Once variables are set they become available within
+	 * the controller class and its "view" files.
+	 *
+	 * @param	array|object|string	$vars
+	 *					An associative array or object containing values
+	 *					to be set, or a value's name if string
+	 * @param 	string	$val	Value to set, only used if $vars is a string
+	 * @return	object
+	 */
+	public function vars($vars, $val = '')
+	{
+		if (is_string($vars))
+		{
+			$vars = array($vars => $val);
+		}
+
+		$vars = $this->_ci_object_to_array($vars);
+
+		if (is_array($vars) && count($vars) > 0)
+		{
+			foreach ($vars as $key => $val)
+			{
+				$this->_ci_cached_vars[$key] = $val;
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clear Cached Variables
+	 *
+	 * Clears the cached variables.
+	 *
+	 * @return  CI_Loader
+	 */
+	public function clear_vars()
+	{
+		$this->_ci_cached_vars = array();
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variable
+	 *
+	 * Check if a variable is set and retrieve it.
+	 *
+	 * @param	string	$key	Variable name
+	 * @return	mixed	The variable or NULL if not found
+	 */
+	public function get_var($key)
+	{
+		return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Variables
+	 *
+	 * Retrieves all loaded variables.
+	 *
+	 * @return	array
+	 */
+	public function get_vars()
+	{
+		return $this->_ci_cached_vars;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Helper Loader
+	 *
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helper($helpers = array())
+	{
+		foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
+		{
+			if (isset($this->_ci_helpers[$helper]))
+			{
+				continue;
+			}
+
+			// Is this a helper extension request?
+			$ext_helper = config_item('subclass_prefix').$helper;
+			$ext_loaded = FALSE;
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$ext_helper.'.php'))
+				{
+					include_once($path.'helpers/'.$ext_helper.'.php');
+					$ext_loaded = TRUE;
+				}
+			}
+
+			// If we have loaded extensions - check if the base one is here
+			if ($ext_loaded === TRUE)
+			{
+				$base_helper = BASEPATH.'helpers/'.$helper.'.php';
+				if ( ! file_exists($base_helper))
+				{
+					show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+				}
+
+				include_once($base_helper);
+				$this->_ci_helpers[$helper] = TRUE;
+				log_message('info', 'Helper loaded: '.$helper);
+				continue;
+			}
+
+			// No extensions found ... try loading regular helpers and/or overrides
+			foreach ($this->_ci_helper_paths as $path)
+			{
+				if (file_exists($path.'helpers/'.$helper.'.php'))
+				{
+					include_once($path.'helpers/'.$helper.'.php');
+
+					$this->_ci_helpers[$helper] = TRUE;
+					log_message('info', 'Helper loaded: '.$helper);
+					break;
+				}
+			}
+
+			// unable to load the helper
+			if ( ! isset($this->_ci_helpers[$helper]))
+			{
+				show_error('Unable to load the requested file: helpers/'.$helper.'.php');
+			}
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load Helpers
+	 *
+	 * An alias for the helper() method in case the developer has
+	 * written the plural form of it.
+	 *
+	 * @uses	CI_Loader::helper()
+	 * @param	string|string[]	$helpers	Helper name(s)
+	 * @return	object
+	 */
+	public function helpers($helpers = array())
+	{
+		return $this->helper($helpers);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Language Loader
+	 *
+	 * Loads language files.
+	 *
+	 * @param	string|string[]	$files	List of language file names to load
+	 * @param	string		Language name
+	 * @return	object
+	 */
+	public function language($files, $lang = '')
+	{
+		get_instance()->lang->load($files, $lang);
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Config Loader
+	 *
+	 * Loads a config file (an alias for CI_Config::load()).
+	 *
+	 * @uses	CI_Config::load()
+	 * @param	string	$file			Configuration file name
+	 * @param	bool	$use_sections		Whether configuration values should be loaded into their own section
+	 * @param	bool	$fail_gracefully	Whether to just return FALSE or display an error message
+	 * @return	bool	TRUE if the file was loaded correctly or FALSE on failure
+	 */
+	public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
+	{
+		return get_instance()->config->load($file, $use_sections, $fail_gracefully);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Driver Loader
+	 *
+	 * Loads a driver library.
+	 *
+	 * @param	string|string[]	$library	Driver name(s)
+	 * @param	array		$params		Optional parameters to pass to the driver
+	 * @param	string		$object_name	An optional object name to assign to
+	 *
+	 * @return	object|bool	Object or FALSE on failure if $library is a string
+	 *				and $object_name is set. CI_Loader instance otherwise.
+	 */
+	public function driver($library, $params = NULL, $object_name = NULL)
+	{
+		if (is_array($library))
+		{
+			foreach ($library as $driver)
+			{
+				$this->driver($driver);
+			}
+
+			return $this;
+		}
+		elseif (empty($library))
+		{
+			return FALSE;
+		}
+
+		if ( ! class_exists('CI_Driver_Library', FALSE))
+		{
+			// We aren't instantiating an object here, just making the base class available
+			require BASEPATH.'libraries/Driver.php';
+		}
+
+		// We can save the loader some time since Drivers will *always* be in a subfolder,
+		// and typically identically named to the library
+		if ( ! strpos($library, '/'))
+		{
+			$library = ucfirst($library).'/'.$library;
+		}
+
+		return $this->library($library, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add Package Path
+	 *
+	 * Prepends a parent path to the library, model, helper and config
+	 * path arrays.
+	 *
+	 * @see	CI_Loader::$_ci_library_paths
+	 * @see	CI_Loader::$_ci_model_paths
+	 * @see CI_Loader::$_ci_helper_paths
+	 * @see CI_Config::$_config_paths
+	 *
+	 * @param	string	$path		Path to add
+	 * @param 	bool	$view_cascade	(default: TRUE)
+	 * @return	object
+	 */
+	public function add_package_path($path, $view_cascade = TRUE)
+	{
+		$path = rtrim($path, '/').'/';
+
+		array_unshift($this->_ci_library_paths, $path);
+		array_unshift($this->_ci_model_paths, $path);
+		array_unshift($this->_ci_helper_paths, $path);
+
+		$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;
+
+		// Add config file path
+		$config =& $this->_ci_get_component('config');
+		$config->_config_paths[] = $path;
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Package Paths
+	 *
+	 * Return a list of all package paths.
+	 *
+	 * @param	bool	$include_base	Whether to include BASEPATH (default: FALSE)
+	 * @return	array
+	 */
+	public function get_package_paths($include_base = FALSE)
+	{
+		return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Remove Package Path
+	 *
+	 * Remove a path from the library, model, helper and/or config
+	 * path arrays if it exists. If no path is provided, the most recently
+	 * added path will be removed removed.
+	 *
+	 * @param	string	$path	Path to remove
+	 * @return	object
+	 */
+	public function remove_package_path($path = '')
+	{
+		$config =& $this->_ci_get_component('config');
+
+		if ($path === '')
+		{
+			array_shift($this->_ci_library_paths);
+			array_shift($this->_ci_model_paths);
+			array_shift($this->_ci_helper_paths);
+			array_shift($this->_ci_view_paths);
+			array_pop($config->_config_paths);
+		}
+		else
+		{
+			$path = rtrim($path, '/').'/';
+			foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
+			{
+				if (($key = array_search($path, $this->{$var})) !== FALSE)
+				{
+					unset($this->{$var}[$key]);
+				}
+			}
+
+			if (isset($this->_ci_view_paths[$path.'views/']))
+			{
+				unset($this->_ci_view_paths[$path.'views/']);
+			}
+
+			if (($key = array_search($path, $config->_config_paths)) !== FALSE)
+			{
+				unset($config->_config_paths[$key]);
+			}
+		}
+
+		// make sure the application default paths are still in the array
+		$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
+		$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
+		$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
+		$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Data Loader
+	 *
+	 * Used to load views and files.
+	 *
+	 * Variables are prefixed with _ci_ to avoid symbol collision with
+	 * variables made available to view files.
+	 *
+	 * @used-by	CI_Loader::view()
+	 * @used-by	CI_Loader::file()
+	 * @param	array	$_ci_data	Data to load
+	 * @return	object
+	 */
+	protected function _ci_load($_ci_data)
+	{
+		// Set the default data variables
+		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
+		{
+			$$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
+		}
+
+		$file_exists = FALSE;
+
+		// Set the path to the requested file
+		if (is_string($_ci_path) && $_ci_path !== '')
+		{
+			$_ci_x = explode('/', $_ci_path);
+			$_ci_file = end($_ci_x);
+		}
+		else
+		{
+			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
+			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
+
+			foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
+			{
+				if (file_exists($_ci_view_file.$_ci_file))
+				{
+					$_ci_path = $_ci_view_file.$_ci_file;
+					$file_exists = TRUE;
+					break;
+				}
+
+				if ( ! $cascade)
+				{
+					break;
+				}
+			}
+		}
+
+		if ( ! $file_exists && ! file_exists($_ci_path))
+		{
+			show_error('Unable to load the requested file: '.$_ci_file);
+		}
+
+		// This allows anything loaded using $this->load (views, files, etc.)
+		// to become accessible from within the Controller and Model functions.
+		$_ci_CI =& get_instance();
+		foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
+		{
+			if ( ! isset($this->$_ci_key))
+			{
+				$this->$_ci_key =& $_ci_CI->$_ci_key;
+			}
+		}
+
+		/*
+		 * Extract and cache variables
+		 *
+		 * You can either set variables using the dedicated $this->load->vars()
+		 * function or via the second parameter of this function. We'll merge
+		 * the two types and cache them so that views that are embedded within
+		 * other views can have access to these variables.
+		 */
+		if (is_array($_ci_vars))
+		{
+			$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
+		}
+		extract($this->_ci_cached_vars);
+
+		/*
+		 * Buffer the output
+		 *
+		 * We buffer the output for two reasons:
+		 * 1. Speed. You get a significant speed boost.
+		 * 2. So that the final rendered template can be post-processed by
+		 *	the output class. Why do we need post processing? For one thing,
+		 *	in order to show the elapsed page load time. Unless we can
+		 *	intercept the content right before it's sent to the browser and
+		 *	then stop the timer it won't be accurate.
+		 */
+		ob_start();
+
+		// If the PHP installation does not support short tags we'll
+		// do a little string replacement, changing the short tags
+		// to standard PHP echo statements.
+		if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
+		{
+			echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace(' $this->_ci_ob_level + 1)
+		{
+			ob_end_flush();
+		}
+		else
+		{
+			$_ci_CI->output->append_output(ob_get_contents());
+			@ob_end_clean();
+		}
+
+		return $this;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Loader
+	 *
+	 * @used-by	CI_Loader::library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$class		Class name to load
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
+	{
+		// Get the class name, and while we're at it trim any slashes.
+		// The directory path can be included as part of the class name,
+		// but we don't want a leading slash
+		$class = str_replace('.php', '', trim($class, '/'));
+
+		// Was the path included with the class name?
+		// We look for a slash to determine this
+		if (($last_slash = strrpos($class, '/')) !== FALSE)
+		{
+			// Extract the path
+			$subdir = substr($class, 0, ++$last_slash);
+
+			// Get the filename from the path
+			$class = substr($class, $last_slash);
+		}
+		else
+		{
+			$subdir = '';
+		}
+
+		$class = ucfirst($class);
+
+		// Is this a stock library? There are a few special conditions if so ...
+		if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
+		{
+			return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
+		}
+
+		// Let's search for the requested library file and load it.
+		foreach ($this->_ci_library_paths as $path)
+		{
+			// BASEPATH has already been checked for
+			if ($path === BASEPATH)
+			{
+				continue;
+			}
+
+			$filepath = $path.'libraries/'.$subdir.$class.'.php';
+
+			// Safety: Was the class already loaded by a previous call?
+			if (class_exists($class, FALSE))
+			{
+				// Before we deem this to be a duplicate request, let's see
+				// if a custom object name is being supplied. If so, we'll
+				// return a new instance of the object
+				if ($object_name !== NULL)
+				{
+					$CI =& get_instance();
+					if ( ! isset($CI->$object_name))
+					{
+						return $this->_ci_init_library($class, '', $params, $object_name);
+					}
+				}
+
+//				log_message('debug', $class.' class already loaded. Second attempt ignored.');
+//				return;
+			}
+			// Does the file exist? No? Bummer...
+			if ( ! file_exists($filepath))
+			{
+				continue;
+			}
+
+			include_once($filepath);
+			return $this->_ci_init_library($class, '', $params, $object_name);
+		}
+
+		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
+		if ($subdir === '')
+		{
+			return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
+		}
+
+		// If we got this far we were unable to find the requested class.
+		log_message('error', 'Unable to load the requested class: '.$class);
+		show_error('Unable to load the requested class: '.$class);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Stock Library Loader
+	 *
+	 * @used-by	CI_Loader::_ci_load_library()
+	 * @uses	CI_Loader::_ci_init_library()
+	 *
+	 * @param	string	$library	Library name to load
+	 * @param	string	$file_path	Path to the library filename, relative to libraries/
+	 * @param	mixed	$params		Optional parameters to pass to the class constructor
+	 * @param	string	$object_name	Optional object name to assign to
+	 * @return	void
+	 * 
+	 * modified by ci-phpunit-test
+	 */
+	protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
+	{
+		$prefix = 'CI_';
+
+//		if (class_exists($prefix.$library_name, FALSE))
+		{
+			if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
+			{
+				$prefix = config_item('subclass_prefix');
+			}
+
+			// Before we deem this to be a duplicate request, let's see
+			// if a custom object name is being supplied. If so, we'll
+			// return a new instance of the object
+			if ($object_name !== NULL)
+			{
+				$CI =& get_instance();
+				if ( ! isset($CI->$object_name))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+			}
+
+//			log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
+//			return;
+		}
+
+		$paths = $this->_ci_library_paths;
+		array_pop($paths); // BASEPATH
+		array_pop($paths); // APPPATH (needs to be the first path checked)
+		array_unshift($paths, APPPATH);
+
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
+			{
+				// Override
+				include_once($path);
+//				if (class_exists($prefix.$library_name, FALSE))
+				{
+					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+				}
+//				else
+//				{
+//					log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
+//				}
+			}
+		}
+
+		include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
+
+		// Check for extensions
+		$subclass = config_item('subclass_prefix').$library_name;
+		foreach ($paths as $path)
+		{
+			if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
+			{
+				include_once($path);
+				if (class_exists($subclass, FALSE))
+				{
+					$prefix = config_item('subclass_prefix');
+					break;
+				}
+				else
+				{
+					log_message('debug', $path.' exists, but does not declare '.$subclass);
+				}
+			}
+		}
+
+		return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Internal CI Library Instantiator
+	 *
+	 * @used-by	CI_Loader::_ci_load_stock_library()
+	 * @used-by	CI_Loader::_ci_load_library()
+	 *
+	 * @param	string		$class		Class name
+	 * @param	string		$prefix		Class name prefix
+	 * @param	array|null|bool	$config		Optional configuration to pass to the class constructor:
+	 *						FALSE to skip;
+	 *						NULL to search in config paths;
+	 *						array containing configuration data
+	 * @param	string		$object_name	Optional object name to assign to
+	 * @return	void
+	 */
+	protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
+	{
+		// Is there an associated config file for this class? Note: these should always be lowercase
+		if ($config === NULL)
+		{
+			// Fetch the config paths containing any package paths
+			$config_component = $this->_ci_get_component('config');
+
+			if (is_array($config_component->_config_paths))
+			{
+				$found = FALSE;
+				foreach ($config_component->_config_paths as $path)
+				{
+					// We test for both uppercase and lowercase, for servers that
+					// are case-sensitive with regard to file names. Load global first,
+					// override with environment next
+					if (file_exists($path.'config/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+						$found = TRUE;
+					}
+					elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+					{
+						include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+						$found = TRUE;
+					}
+
+					// Break on the first found configuration, thus package
+					// files are not overridden by default paths
+					if ($found === TRUE)
+					{
+						break;
+					}
+				}
+			}
+		}
+
+		$class_name = $prefix.$class;
+
+		// Is the class name valid?
+		if ( ! class_exists($class_name, FALSE))
+		{
+			log_message('error', 'Non-existent class: '.$class_name);
+			show_error('Non-existent class: '.$class_name);
+		}
+
+		// Set the variable name we will assign the class to
+		// Was a custom class name supplied? If so we'll use it
+		if (empty($object_name))
+		{
+			$object_name = strtolower($class);
+			if (isset($this->_ci_varmap[$object_name]))
+			{
+				$object_name = $this->_ci_varmap[$object_name];
+			}
+		}
+
+		// Don't overwrite existing properties
+		$CI =& get_instance();
+		if (isset($CI->$object_name))
+		{
+			if ($CI->$object_name instanceof $class_name)
+			{
+				log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+				return;
+			}
+
+			show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
+		}
+
+		// Save the class name and object name
+		$this->_ci_classes[$object_name] = $class;
+
+		// Instantiate the class
+		$CI->$object_name = isset($config)
+			? new $class_name($config)
+			: new $class_name();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Autoloader
+	 *
+	 * Loads component listed in the config/autoload.php file.
+	 *
+	 * @used-by	CI_Loader::initialize()
+	 * @return	void
+	 */
+	protected function _ci_autoloader()
+	{
+		if (file_exists(APPPATH.'config/autoload.php'))
+		{
+			include(APPPATH.'config/autoload.php');
+		}
+
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+		}
+
+		if ( ! isset($autoload))
+		{
+			return;
+		}
+
+		// Autoload packages
+		if (isset($autoload['packages']))
+		{
+			foreach ($autoload['packages'] as $package_path)
+			{
+				$this->add_package_path($package_path);
+			}
+		}
+
+		// Load any custom config file
+		if (count($autoload['config']) > 0)
+		{
+			foreach ($autoload['config'] as $val)
+			{
+				$this->config($val);
+			}
+		}
+
+		// Autoload helpers and languages
+		foreach (array('helper', 'language') as $type)
+		{
+			if (isset($autoload[$type]) && count($autoload[$type]) > 0)
+			{
+				$this->$type($autoload[$type]);
+			}
+		}
+
+		// Autoload drivers
+		if (isset($autoload['drivers']))
+		{
+			foreach ($autoload['drivers'] as $item)
+			{
+				$this->driver($item);
+			}
+		}
+
+		// Load libraries
+		if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
+		{
+			// Load the database driver.
+			if (in_array('database', $autoload['libraries']))
+			{
+				$this->database();
+				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
+			}
+
+			// Load all other libraries
+			$this->library($autoload['libraries']);
+		}
+
+		// Autoload models
+		if (isset($autoload['model']))
+		{
+			$this->model($autoload['model']);
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Object to Array translator
+	 *
+	 * Takes an object as input and converts the class variables to
+	 * an associative array with key/value pairs.
+	 *
+	 * @param	object	$object	Object data to translate
+	 * @return	array
+	 */
+	protected function _ci_object_to_array($object)
+	{
+		return is_object($object) ? get_object_vars($object) : $object;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * CI Component getter
+	 *
+	 * Get a reference to a specific library or model.
+	 *
+	 * @param 	string	$component	Component name
+	 * @return	bool
+	 */
+	protected function &_ci_get_component($component)
+	{
+		$CI =& get_instance();
+		return $CI->$component;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Prep filename
+	 *
+	 * This function prepares filenames of various items to
+	 * make their loading more reliable.
+	 *
+	 * @param	string|string[]	$filename	Filename(s)
+	 * @param 	string		$extension	Filename extension
+	 * @return	array
+	 */
+	protected function _ci_prep_filename($filename, $extension)
+	{
+		if ( ! is_array($filename))
+		{
+			return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension));
+		}
+		else
+		{
+			foreach ($filename as $key => $val)
+			{
+				$filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension);
+			}
+
+			return $filename;
+		}
+	}
+
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/helpers/url_helper.php b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/helpers/url_helper.php
new file mode 100644
index 00000000000..0dec857c0d2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/_ci_phpunit_test/replacing/helpers/url_helper.php
@@ -0,0 +1,111 @@
+
+ * @author		EllisLab Dev Team
+ * @link		https://codeigniter.com/user_guide/helpers/url_helper.html
+ */
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('redirect'))
+{
+	function redirect($uri = '', $method = 'auto', $code = NULL)
+	{
+		if ( ! preg_match('#^(\w+:)?//#i', $uri))
+		{
+			$uri = site_url($uri);
+		}
+
+		// IIS environment likely? Use 'refresh' for better compatibility
+		if ($method === 'auto' && isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== FALSE)
+		{
+			$method = 'refresh';
+		}
+		elseif ($method !== 'refresh' && (empty($code) OR ! is_numeric($code)))
+		{
+			if (isset($_SERVER['SERVER_PROTOCOL'], $_SERVER['REQUEST_METHOD']) && $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1')
+			{
+				$code = ($_SERVER['REQUEST_METHOD'] !== 'GET')
+					? 303	// reference: http://en.wikipedia.org/wiki/Post/Redirect/Get
+					: 307;
+			}
+			else
+			{
+				$code = 302;
+			}
+		}
+
+		switch ($method)
+		{
+			case 'refresh':
+				if (ENVIRONMENT !== 'testing')
+				{
+					header('Refresh:0;url='.$uri);
+				}
+				break;
+			default:
+				if (ENVIRONMENT !== 'testing')
+				{
+					header('Location: '.$uri, TRUE, $code);
+				}
+				break;
+		}
+
+		if (ENVIRONMENT !== 'testing')
+		{
+			exit;
+		}
+		else
+		{
+			while (ob_get_level() > 1)
+			{
+				ob_end_clean();
+			}
+
+			throw new CIPHPUnitTestRedirectException('Redirect to ' . $uri, $code);
+		}
+	}
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/controllers/Welcome_test.php b/vendor/kenjis/ci-phpunit-test/application/tests/controllers/Welcome_test.php
new file mode 100644
index 00000000000..35dce137ca9
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/controllers/Welcome_test.php
@@ -0,0 +1,35 @@
+
+ * @license    MIT License
+ * @copyright  2015 Kenji Suzuki
+ * @link       https://github.com/kenjis/ci-phpunit-test
+ */
+
+class Welcome_test extends TestCase
+{
+	public function test_index()
+	{
+		$output = $this->request('GET', ['Welcome', 'index']);
+		$this->assertContains('Welcome to CodeIgniter', $output);
+	}
+
+	public function test_method_404()
+	{
+		$this->request('GET', ['Welcome', 'method_not_exist']);
+		$this->assertResponseCode(404);
+	}
+
+	public function test_APPPATH()
+	{
+		$actual = realpath(APPPATH);
+		$expected = realpath(__DIR__ . '/../..');
+		$this->assertEquals(
+			$expected,
+			$actual,
+			'Your APPPATH seems to be wrong. Check your $application_folder in tests/Bootstrap.php'
+		);
+	}
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/helpers/.gitkeep b/vendor/kenjis/ci-phpunit-test/application/tests/helpers/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/hooks/.gitkeep b/vendor/kenjis/ci-phpunit-test/application/tests/hooks/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/libraries/.gitkeep b/vendor/kenjis/ci-phpunit-test/application/tests/libraries/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/mocks/autoloader.php b/vendor/kenjis/ci-phpunit-test/application/tests/mocks/autoloader.php
new file mode 100644
index 00000000000..f6b2cfd0530
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/mocks/autoloader.php
@@ -0,0 +1,121 @@
+
+ * @license    MIT License
+ * @copyright  2015 Kenji Suzuki
+ * @link       https://github.com/kenjis/ci-phpunit-test
+ */
+
+class Mock_Libraries_Email
+{
+	private $data = [];
+
+	/**
+	 * @var bool return value of send()
+	 */
+	public $return_send = TRUE;
+
+	public function initialize()
+	{
+		
+	}
+
+	public function from($from)
+	{
+		$this->data['from'] = $from;
+	}
+
+	public function to($to)
+	{
+		$this->data['to'] = $to;
+	}
+
+	public function bcc($bcc)
+	{
+		$this->data['bcc'] = $bcc;
+	}
+
+	public function subject($subject)
+	{
+		$this->data['subject'] = $subject;
+	}
+
+	public function message($message)
+	{
+		$this->data['message'] = $message;
+	}
+
+	public function send()
+	{
+		return $this->return_send;
+	}
+
+	public function _get_data()
+	{
+		return $this->data;
+	}
+}
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/models/.gitkeep b/vendor/kenjis/ci-phpunit-test/application/tests/models/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/vendor/kenjis/ci-phpunit-test/application/tests/phpunit.xml b/vendor/kenjis/ci-phpunit-test/application/tests/phpunit.xml
new file mode 100644
index 00000000000..ea199cdc03d
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/application/tests/phpunit.xml
@@ -0,0 +1,25 @@
+
+	
+		
+			./
+			./_ci_phpunit_test/
+		
+	
+	
+		
+			../controllers
+			../models
+			../views
+			../libraries
+			../helpers
+			../hooks
+		
+	
+	
+		
+		
+		
+	
+
diff --git a/vendor/kenjis/ci-phpunit-test/bin/check-ci-diff.sh b/vendor/kenjis/ci-phpunit-test/bin/check-ci-diff.sh
new file mode 100644
index 00000000000..578f2f48d04
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/bin/check-ci-diff.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+usage() {
+	echo "Get diff for CodeIgniter two versions"
+	echo " usage: $0  "
+	echo "    eg: $0 CodeIgniter-3.0.0.zip CodeIgniter-3.0.1.zip"
+}
+
+if [ $# -eq 0 ]; then
+	usage
+	exit
+fi
+
+f1="$1"
+f2="$2"
+v1=${f1%.*}
+v2=${f2%.*}
+
+rm -rf "$v1" "$v2"
+
+unzip "$v1"
+unzip "$v2"
+
+rm -rf "$v1/user_guide"
+rm -rf "$v2/user_guide"
+diff -uwbrN "$v1" "$v2" > "$v1-$v2.diff"
+
+# Please add files which you modify.
+list="
+index.php
+system/core/Loader.php
+system/core/Input.php
+system/core/Common.php
+system/core/CodeIgniter.php
+system/helpers/url_helper.php
+"
+
+diff="$v1-$v2.ci-phpunit-test-only.diff"
+echo -n > "$diff"
+
+for i in $list
+do
+	diff -uwb "$v1/$i" "$v2/$i" >> "$diff"
+done
diff --git a/vendor/kenjis/ci-phpunit-test/bin/check-diff.sh b/vendor/kenjis/ci-phpunit-test/bin/check-diff.sh
new file mode 100644
index 00000000000..67bd652907d
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/bin/check-diff.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+cd `dirname $0`
+cd ..
+
+diff -u ../../codeigniter/framework/index.php application/tests/Bootstrap.php 
+diff -u ../../codeigniter/framework/system/core/Loader.php application/tests/_ci_phpunit_test/replacing/core/Loader.php
+diff -u ../../codeigniter/framework/system/core/Input.php application/tests/_ci_phpunit_test/replacing/core/Input.php
+diff -u ../../codeigniter/framework/system/core/CodeIgniter.php application/tests/_ci_phpunit_test/replacing/core/CodeIgniter.php
diff --git a/vendor/kenjis/ci-phpunit-test/composer.json b/vendor/kenjis/ci-phpunit-test/composer.json
new file mode 100644
index 00000000000..a23f1d2b3b2
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "kenjis/ci-phpunit-test",
+    "description": "An easier way to use PHPUnit with CodeIgniter 3.0",
+    "keywords": [
+        "phpunit",
+        "unit testing",
+        "test",
+        "codeigniter",
+        "monkey patch"
+    ],
+    "homepage": "http://kenjis.github.io/ci-phpunit-test/",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Kenji Suzuki",
+            "homepage": "https://github.com/kenjis"
+        }
+    ],
+    "support": {
+        "forum": "http://forum.codeigniter.com/thread-61725.html"
+    },
+    "require": {
+        "php": ">=5.4.0"
+    },
+    "replace": {
+        "nikic/php-parser": "2.0.1"
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/kenjis/ci-phpunit-test/docs/ChangeLog.md b/vendor/kenjis/ci-phpunit-test/docs/ChangeLog.md
new file mode 100644
index 00000000000..24809bf7930
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/docs/ChangeLog.md
@@ -0,0 +1,282 @@
+# Change Log for ci-phpunit-test
+
+## v0.12.1 (2016/06/11)
+
+### Fixed
+
+* Fix bug that *Function Patcher* on `openssl_random_pseudo_bytes()` may cause "Warning: Missing argument 2". See [#119](https://github.com/kenjis/ci-phpunit-test/issues/119).
+* Fix bug that installation/update script for Composer installation causes "Notice: Undefined offset: 1".
+
+## v0.12.0 (2016/04/17)
+
+### Added
+
+* Monkey Patching on constants. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.12.0/docs/HowToWriteTests.md#patching-constants).
+
+### Others
+
+* Update nikic/PHP-Parser to v2.0.1
+
+## v0.11.3 (2016/03/25)
+
+### Fixed
+
+* `assertRedirect()` does not work with external redirects. See [#104](https://github.com/kenjis/ci-phpunit-test/pull/104).
+
+### Others
+
+* Compatible with CodeIgniter 3.0.6
+* Improved installer. See [#103](https://github.com/kenjis/ci-phpunit-test/pull/103).
+
+## v0.11.2 (2016/03/17)
+
+### Others
+
+* Compatible with CodeIgniter 3.0.5
+
+## v0.11.1 (2016/02/22)
+
+### Fixed
+
+* Fix bug that `$this->input->get_request_header()` returns the first header value for all tests. See [#92](https://github.com/kenjis/ci-phpunit-test/issues/92).
+* Fix bug that config values are not reset between tests. See [#94](https://github.com/kenjis/ci-phpunit-test/issues/94).
+*  Fix bug that `CI_Output::_display()` is called even if you call a controller method directly (when you pass an array to the 2nd argument of `$this->request()`).
+
+### Others
+
+* Improved documentation for `$this->request()`.
+
+## v0.11.0 (2016/01/20)
+
+### Upgrade Note
+
+* Now ci-phpunit-test replaces `CI_Input`. If you use MY_Input, see [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.11.0/docs/HowToWriteTests.md#my_input).
+* If you use *Monkey Patching*, please update `tests/Bootstrap.php`. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.11.0/docs/HowToWriteTests.md#upgrade-note-for-v0110).
+* If you use PsySH v0.5, please update to v0.6.
+
+### Added
+
+* `$this->assertResponseCookie()` to assert HTTP response cookies. See [#88](https://github.com/kenjis/ci-phpunit-test/pull/88).
+* Now `$this->request->enableHooks()` calls hook `display_override`.
+* `$this->request->addCallablePreConstructor()` to add callable.
+* Now *Moneky Patching* can patch code with PHP 7 new syntax.
+* `header()` and `setcookie()` are added to *Function Patcher*'s white list.
+
+### Fixed
+
+* `_output()` method in controllers does not work in controller testing.
+
+### Others
+
+* Compatible with CodeIgniter 3.0.4
+* Update nikic/PHP-Parser to v2.0.0
+
+## v0.10.1 (2015/12/31)
+
+### Fixed
+
+* Fix bug that global variables for core classes are null. See [#75](https://github.com/kenjis/ci-phpunit-test/issues/75).
+* Fix bug that can't use constant in `config.php`. See [#78](https://github.com/kenjis/ci-phpunit-test/issues/78).
+* Fix bug that can't autoload library with alternative library name. See [#79](https://github.com/kenjis/ci-phpunit-test/pull/79).
+* Fix bug that *Function Patcher* on `openssl_random_pseudo_bytes()` which returns `null` does not work.
+
+## v0.10.0 (2015/11/27)
+
+### Fixed
+
+* Fix wrong implementation of resetting CodeIgniter instance. Now `reset_instance()` removes the existing *CodeIgniter instance*. See [#74](https://github.com/kenjis/ci-phpunit-test/pull/74).
+
+### Changed
+
+* Now `$this->getDouble()` does not call the original constructor by default. See [Function/Class Reference](https://github.com/kenjis/ci-phpunit-test/blob/v0.10.0/docs/FunctionAndClassReference.md#testcasegetdoubleclassname-params-enable_constructor--false).
+* Now `reset_instance()` removes the existing *CodeIgniter instance*. See [#74](https://github.com/kenjis/ci-phpunit-test/pull/74).
+
+### Added
+
+* NetBeans test suite provider `application/tests/_ci_phpunit_test/TestSuiteProvider.php`. To use it, go to *Project Properties* > *Testing*, check *Use Custom Test Suite* checkbox, and select the file.
+
+## v0.9.1 (2015/11/22)
+
+### Fixed
+
+* Fix bug that `phpunit` dies when `Unable to locate the specified class` error.
+
+### Others
+
+* Improved documentation.
+
+## v0.9.0 (2015/11/18)
+
+### Added
+
+* `$this->request->addCallable()` to add callable. See [#68](https://github.com/kenjis/ci-phpunit-test/pull/68).
+* Autoloading classes in `application/modules` folder.
+* You can configure search paths for autoloader. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.9.0/docs/HowToWriteTests.md#autoloader).
+
+### Others
+
+* Compatible with CodeIgniter 3.0.3
+
+## v0.8.2 (2015/10/09)
+
+### Fixed
+
+* Fix bug that monkey patching changes original source code in some cases of heredoc/nowdoc strings.
+
+### Others
+
+* Compatible with CodeIgniter 3.0.2
+* Compatible with PHP 7.0.0-RC4
+* Update nikic/PHP-Parser to v1.4.1
+
+## v0.8.1 (2015/10/01)
+
+### Fixed
+
+* Fix bug that `$route['404_override']` controller/method is called in Bootstrap. See [#63](https://github.com/kenjis/ci-phpunit-test/pull/63).
+
+## v0.8.0 (2015/09/28)
+
+### Changed
+
+* Better support for SQLite in-memory database. Now `reset_instance()` does not close SQLite in-memory database connection.
+
+### Fixed
+
+* Fix bug that `$this->getDouble()` can't create mocks which have methods named method.
+* Fix bug that monkey patching which returns `null` does not work.
+
+### Removed
+
+* Property `$bc_mode_throw_PHPUnit_Framework_Exception` in `CIPHPUnitTestRequest` class (deprecated since v0.4.0). See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/HowToWriteTests.md#show_error-and-show_404).
+
+## v0.7.0 (2015/09/09)
+
+### Upgrade Note
+
+* Please update `application/tests/phpunit.xml`. Replace it or apply [this patch](https://github.com/kenjis/ci-phpunit-test/commit/7af9e330251e2ab72a631f4d5f92a41c0ad37aca). See [#52](https://github.com/kenjis/ci-phpunit-test/pull/52).
+
+### Changed
+
+* Now `$this->warningOff()` turns off Notice, too.
+
+### Added
+
+* Now `$this->request()` can create REST request more easily. See [#47](https://github.com/kenjis/ci-phpunit-test/pull/47).
+* `$this->request->setHeader()` to set HTTP request header. See [#47](https://github.com/kenjis/ci-phpunit-test/pull/47).
+* `$this->assertResponseHeader()` to assert HTTP response header. See [#47](https://github.com/kenjis/ci-phpunit-test/pull/47).
+* You can add query string in URI string of `$this->request()`. See [#51](https://github.com/kenjis/ci-phpunit-test/pull/51).
+* Autoloading for libraries
+* Add `application/libraries/Session/MY_Session.php` as a sample
+* `ReflectionHelper` class to access non-public method or property. See [Function/Class Reference](https://github.com/kenjis/ci-phpunit-test/blob/v0.7.0/docs/FunctionAndClassReference.md#class-reflectionhelper).
+
+### Fixed
+
+* `$this->request()` returns null when `show_404()` or `show_error()` is called. Now it returns error message.
+* `$this->CI` in `TestCase` class after calling `$this->request()` is still the previous instance. [#50](https://github.com/kenjis/ci-phpunit-test/issues/50).
+* Autoloader only searches class files only in top level and sub folder for them. [#48](https://github.com/kenjis/ci-phpunit-test/issues/48).
+* `set_status_header()` calls `header()` if `is_cli()` returns false.
+* Fix `phpunit.xml`. See [#52](https://github.com/kenjis/ci-phpunit-test/pull/52).
+
+### Removed
+
+* 4th param `$callable` of `$this->request()` and `$this->ajaxRequest()` (deprecated since v0.3.0)  
+  Use `$this->request->setCallable()` method instead. See [Function/Class Reference](https://github.com/kenjis/ci-phpunit-test/blob/v0.3.0/docs/FunctionAndClassReference.md#testcaserequestmethod-argv-params---callable--null).
+
+### Others
+
+* Add documentation for CodeIgniter Rest Server. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.7.0/docs/HowToWriteTests.md#codeigniter-rest-server).
+* Compatible with PsySH v0.5.2
+
+## v0.6.2 (2015/08/13)
+
+### Fixed
+
+* Fix bug that can't test model classes (classes in `application/models` folder) which do not extend `CI_Model` more than once.
+
+## v0.6.1 (2015/08/12)
+
+### Changed
+
+* How to enable Monkey Patching has been changed. `TestCase::$enable_patcher` was removed. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.6.1/docs/HowToWriteTests.md#monkey-patching).
+
+### Added
+
+* Monkey Patching on functions. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.6.1/docs/HowToWriteTests.md#patching-functions).
+* Monkey Patching on methods in user-defined classes. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.6.1/docs/HowToWriteTests.md#patching-methods-in-user-defined-classes).
+* `$this->resetInstance()` for better model testing. See [#40](https://github.com/kenjis/ci-phpunit-test/pull/40).
+
+### Removed
+
+* `TestCase::$enable_patcher` (Introduced in v0.5.0)
+
+### Others
+
+* Compatible with CodeIgniter 3.0.1
+
+## v0.5.0 (2015/07/27)
+
+### Changed
+
+* Now ci-phpunit-test replaces `redirect()` function by default. See [#33](https://github.com/kenjis/ci-phpunit-test/pull/33).
+
+### Added
+
+* Monkey Patching on `exit()`. ci-phpunit-test could convert `exit()` in your code to Exception on the fly. See [#32](https://github.com/kenjis/ci-phpunit-test/pull/32).
+* `$this->request->setCallablePreConstructor()` to inject mocks into your controller constructors. See [#29](https://github.com/kenjis/ci-phpunit-test/pull/29).
+
+### Fixed
+
+* Fix bug that PHPUnit debug info of the first test is not outputted.
+
+### Removed
+
+* `get_new_instance()` (deprecated since pre v0.1.0)
+
+## v0.4.0 (2015/07/21)
+
+### Changed
+
+* Changed `MY_url_helper.php` as sample. If you use new `MY_url_helper.php`, you must update your tests for `redirect()` using new `$this->assertRedirect()` method. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/HowToWriteTests.md#redirect) and [#28](https://github.com/kenjis/ci-phpunit-test/pull/28).
+* Changed how to test `show_404()` and `show_error()`. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/HowToWriteTests.md#show_error-and-show_404) and [#28](https://github.com/kenjis/ci-phpunit-test/pull/28).
+
+### Added
+
+* `$this->assertResponseCode()` to check response code in controller tests. See [Function/Class Reference](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/FunctionAndClassReference.md#testcaseassertresponsecodecode).
+* `$this->assertRedirect()` to check if `redirect()` is called in controller tests. See [Function/Class Reference](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/FunctionAndClassReference.md#testcaseassertredirecturi-code--null).
+* Property `$bc_mode_throw_PHPUnit_Framework_Exception` in `CIPHPUnitTestRequest` class
+
+### Deprecated
+
+* Property `$bc_mode_throw_PHPUnit_Framework_Exception` in `CIPHPUnitTestRequest` class
+
+### Others
+
+* Improved documentation. See [How to Write Test](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/HowToWriteTests.md).
+
+## v0.3.0 (2015/07/14)
+
+### Deprecated
+
+* 4th param `$callable` of `$this->request()` and `$this->ajaxRequest()`. Use `$this->request->setCallable()` method instead. See [Function/Class Reference](https://github.com/kenjis/ci-phpunit-test/blob/v0.3.0/docs/FunctionAndClassReference.md#testcaserequestmethod-argv-params---callable--null).
+
+### Added
+
+* `$this->request->setCallable()` See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.3.0/docs/HowToWriteTests.md#request-and-use-mocks).
+* You can enable hooks for controller in controller testing. `$this->request->enableHooks()` is added. See [How to Write Tests](https://github.com/kenjis/ci-phpunit-test/blob/v0.3.0/docs/HowToWriteTests.md#controller-with-hooks).
+
+## v0.2.0 (2015/06/19)
+
+* Change `MY_url_helper.php` as sample. If you use new `MY_url_helper.php`, you must catch `PHPUnit_Framework_Exception` when you test code using `redirect()`.
+* Improve documentation
+
+## v0.1.1 (2015/06/15)
+
+* Improve installation. See [Installation](https://github.com/kenjis/ci-phpunit-test#installation).
+* Fix bug that Bootstrap outputs 404 page when 404_override
+* Fix bug that risky tests occur [#14](https://github.com/kenjis/ci-phpunit-test/issues/14)
+
+## v0.1.0 (2015/06/12)
+
+* Initial version
+* Compatible with CodeIgniter 3.0.0
diff --git a/vendor/kenjis/ci-phpunit-test/docs/FunctionAndClassReference.md b/vendor/kenjis/ci-phpunit-test/docs/FunctionAndClassReference.md
new file mode 100644
index 00000000000..7a85f86bffa
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/docs/FunctionAndClassReference.md
@@ -0,0 +1,728 @@
+# ci-phpunit-test for CodeIgniter 3.0
+
+version: **v0.12.1** | 
+[v0.11.3](https://github.com/kenjis/ci-phpunit-test/blob/v0.11.3/docs/FunctionAndClassReference.md) | 
+[v0.10.1](https://github.com/kenjis/ci-phpunit-test/blob/v0.10.1/docs/FunctionAndClassReference.md) | 
+[v0.9.1](https://github.com/kenjis/ci-phpunit-test/blob/v0.9.1/docs/FunctionAndClassReference.md) | 
+[v0.8.2](https://github.com/kenjis/ci-phpunit-test/blob/v0.8.2/docs/FunctionAndClassReference.md) | 
+[v0.7.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.7.0/docs/FunctionAndClassReference.md) | 
+[v0.6.2](https://github.com/kenjis/ci-phpunit-test/blob/v0.6.2/docs/FunctionAndClassReference.md) | 
+[v0.5.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.5.0/docs/FunctionAndClassReference.md) | 
+[v0.4.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/FunctionAndClassReference.md) | 
+[v0.3.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.3.0/docs/FunctionAndClassReference.md) | 
+[v0.2.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.2.0/docs/FunctionAndClassReference.md)
+
+## Function/Class Reference
+
+- [*function* `reset_instance()`](#function-reset_instance)
+- [*function* `set_is_cli($return)`](#function-set_is_clireturn)
+- [*function* `load_class_instance($classname, $instance)`](#function-load_class_instanceclassname-instance)
+- [*class* TestCase](#class-testcase)
+	- [`TestCase::resetInstance()`](#testcaseresetinstance)
+	- [`TestCase::request($method, $argv, $params = [])`](#testcaserequestmethod-argv-params--)
+		- [`request->setHeader()`](#request-setheader)
+		- [`request->setCallable()`](#request-setcallable)
+		- [`request->addCallable()`](#request-addcallable)
+		- [`request->setCallablePreConstructor()`](#request-setcallablepreconstructor)
+		- [`request->addCallablePreConstructor()`](#request-addcallablepreconstructor)
+		- [`request->enableHooks()`](#request-enablehooks)
+	- [`TestCase::ajaxRequest($method, $argv, $params = [])`](#testcaseajaxrequestmethod-argv-params--)
+	- [`TestCase::assertResponseCode($code)`](#testcaseassertresponsecodecode)
+	- [`TestCase::assertRedirect($uri, $code = null)`](#testcaseassertredirecturi-code--null)
+	- [`TestCase::assertResponseHeader($name, $value)`](#testcaseassertresponseheadername-value)
+	- [`TestCase::assertResponseCookie($name, $value, $allow_duplicate = false)`](#testcaseassertresponsecookiename-value-allow_duplicate--false)
+	- [`TestCase::getDouble($classname, $params, $enable_constructor = false)`](#testcasegetdoubleclassname-params-enable_constructor--false)
+	- [`TestCase::verifyInvoked($mock, $method, $params)`](#testcaseverifyinvokedmock-method-params)
+	- [`TestCase::verifyInvokedOnce($mock, $method, $params)`](#testcaseverifyinvokedoncemock-method-params)
+	- [`TestCase::verifyInvokedMultipleTimes($mock, $method, $times, $params)`](#testcaseverifyinvokedmultipletimesmock-method-times-params)
+	- [`TestCase::verifyNeverInvoked($mock, $method, $params)`](#testcaseverifyneverinvokedmock-method-params)
+	- [`TestCase::warningOff()`](#testcasewarningoff)
+	- [`TestCase::warningOn()`](#testcasewarningon)
+- [*class* ReflectionHelper](#class-reflectionhelper)
+	- [`ReflectionHelper::getPrivateProperty($obj, $property)`](#reflectionhelpergetprivatepropertyobj-property)
+	- [`ReflectionHelper::setPrivateProperty($obj, $property, $value)`](#reflectionhelpersetprivatepropertyobj-property-value)
+	- [`ReflectionHelper::getPrivateMethodInvoker($obj, $method)`](#reflectionhelpergetprivatemethodinvokerobj-method)
+- [*class* MonkeyPatch](#class-monkeypatch)
+	- [`MonkeyPatch::patchFunction($function, $return_value, $class_method)`](#monkeypatchpatchfunctionfunction-return_value-class_method)
+	- [`MonkeyPatch::resetFunctions()`](#monkeypatchresetfunctions)
+	- [`MonkeyPatch::patchMethod($classname, $params)`](#monkeypatchpatchmethodclassname-params)
+	- [`MonkeyPatch::resetMethods()`](#monkeypatchresetmethods)
+	- [`MonkeyPatch::patchConstant($constant, $value, $class_method)`](#monkeypatchpatchconstantconstant-value-class_method)
+	- [`MonkeyPatch::resetConstants()`](#monkeypatchresetconstants)
+	- [`MonkeyPatch::verifyInvoked($class_method, $params)`](#monkeypatchverifyinvokedclass_method-params)
+	- [`MonkeyPatch::verifyInvokedOnce($class_method, $params)`](#monkeypatchverifyinvokedonceclass_method-params)
+	- [`MonkeyPatch::verifyInvokedMultipleTimes($class_method, $times, $params)`](#monkeypatchverifyinvokedmultipletimesclass_method-times-params)
+	- [`MonkeyPatch::verifyNeverInvoked($class_method, $params)`](#monkeypatchverifyneverinvokedclass_method-params)
+
+### *function* `reset_instance()`
+
+Resets CodeIgniter instance. You must create a new controller instance after calling this function.
+
+~~~php
+reset_instance();
+$controller = new Welcome();
+$this->CI =& get_instance();
+~~~
+
+Normally, you don't have to use this function. Use [`TestCase::resetInstance()`](#testcaseresetinstance) method instead.
+
+**Note:** Before you create a new controller instance, `get_instance()` returns `CIPHPUnitTestNullCodeIgniter` object.
+
+### *function* `set_is_cli($return)`
+
+| param   | type | description         |
+|---------|------|---------------------|
+|`$return`| bool | return value to set |
+
+Sets return value of `is_cli()` function.
+
+~~~php
+set_is_cli(FALSE);
+~~~
+
+### *function* `load_class_instance($classname, $instance)`
+
+| param      | type   | description     |
+|------------|--------|-----------------|
+|`$classname`| string | class name      |
+|`$instance` | object | object instance |
+
+Injects an instance directly into `load_class()` function.
+
+~~~php
+$email = $this->getMockBuilder('CI_Email')
+	->setMethods(['send'])
+	->getMock();
+$email->method('send')
+	->willReturn(TRUE);
+load_class_instance('email', $email);
+~~~
+
+### *class* TestCase
+
+#### `TestCase::resetInstance()`
+
+Resets CodeIgniter instance and assign new CodeIgniter instance as `$this->CI`.
+
+~~~php
+public function setUp()
+{
+	$this->resetInstance();
+	$this->CI->load->model('Category_model');
+	$this->obj = $this->CI->Category_model;
+}
+~~~
+
+**Note:** When you call [$this->request()](#testcaserequestmethod-argv-params--), you don't have to use this method. Because `$this->request()` resets CodeIgniter instance internally.
+
+**Upgrade Note for v0.6.0**
+
+Before v0.6.0, we write `setUp()` method like this:
+
+~~~php
+public function setUp()
+{
+	$this->CI =& get_instance();
+	$this->CI->load->model('Category_model');
+	$this->obj = $this->CI->Category_model;
+}
+~~~
+
+When you use the way, you use the same CodeIgniter instance and the same `Category_model` instance in every test method.
+
+In contrast, if you use `$this->resetInstance()`, it resets CodeIgniter instance and `Category_model`. So you use new CodeIgniter instance and new `Category_model` instance in every test method.
+
+#### `TestCase::request($method, $argv, $params = [])`
+
+| param     | type         | description                                   |
+|-----------|--------------|-----------------------------------------------|
+|`$method`  | string       | HTTP method                                   |
+|`$argv`    | array/string | controller, method [, arg1, ...] / URI string |
+|`$params`  | array/string | POST params or GET params / raw_input_stream  |
+
+`returns` (string) output strings (view)
+
+Runs a controller method or make a request to URI string, after `reset_instance()`.
+
+If you want to invoke routing, specify URI string:
+
+~~~php
+$output = $this->request('GET', 'products/shoes/show/123');
+~~~
+
+You could add query string in URI string:
+
+~~~php
+$output = $this->request('GET', 'users/detail?name=John+O%27Reilly');
+~~~
+
+If you want to make POST request:
+
+~~~php
+$output = $this->request(
+	'POST',
+	'form/index',
+	['name' => 'John Smith', 'email' => 'john@example.com']
+);
+~~~
+
+If you want to call a controller method directly:
+
+~~~php
+$output = $this->request('GET', ['Form', 'index']);
+~~~
+
+**Note:** If you pass an array to the 2nd argument, it does not invoke routing, `_remap()` and `_output()` methods.
+
+##### `request->setHeader()`
+
+Sets HTTP request header.
+
+~~~php
+$this->request->setHeader('Accept', 'application/csv');
+~~~
+
+##### `request->setCallable()`
+
+Sets (and resets) a function (callable) to run after controller instantiation.
+
+~~~php
+$this->request->setCallable(
+	function ($CI) {
+		$CI->load->library('user_agent');
+	};
+);
+$output = $this->request('GET', ['Bbs', 'index']);
+~~~
+
+You can set one callable with `$this->request->setCallable()`. If you want to add more than one callable, you can use `$this->request->addCallable()` below.
+
+##### `request->addCallable()`
+
+Adds a function (callable) to run after controller instantiation.
+
+~~~php
+$this->request->addCallable(
+	function ($CI) {
+		$CI->load->library('user_agent');
+	};
+);
+$output = $this->request('GET', ['Bbs', 'index']);
+~~~
+
+##### `request->setCallablePreConstructor()`
+
+Sets (and resets) a function to run before controller instantiation.
+
+~~~php
+$this->request->setCallablePreConstructor(
+	function () {
+		// Get mock object
+		$auth = $this->getDouble(
+			'Ion_auth', ['logged_in' => TRUE]
+		);
+		// Inject mock object
+		load_class_instance('ion_auth', $auth);
+	}
+);
+~~~
+
+##### `request->addCallablePreConstructor()`
+
+Adds a function (callable) to run before controller instantiation.
+
+~~~php
+$this->request->addCallablePreConstructor(
+	function () {
+		// Get mock object
+		$auth = $this->getDouble(
+			'Ion_auth', ['logged_in' => TRUE]
+		);
+		// Inject mock object
+		load_class_instance('ion_auth', $auth);
+	}
+);
+~~~
+
+##### `request->enableHooks()`
+
+If you want to enable hooks, call `$this->request->enableHooks()` method. It enables only `pre_controller`, `post_controller_constructor`, `post_controller` and `display_override` hooks.
+
+~~~php
+$this->request->enableHooks();
+$output = $this->request('GET', 'products/shoes/show/123');
+~~~
+
+#### `TestCase::ajaxRequest($method, $argv, $params = [])`
+
+| param     | type         | description                                   |
+|-----------|--------------|-----------------------------------------------|
+|`$method`  | string       | HTTP method                                   |
+|`$argv`    | array/string | controller, method [, arg1, ...] / URI string |
+|`$params`  | array/string | POST params or GET params / raw_input_stream  |
+
+`returns` (string) output strings
+
+The same as `TestCase::request()`, but this makes an Ajax request. This adds only `$_SERVER['HTTP_X_REQUESTED_WITH']`.
+
+~~~php
+$output = $this->ajaxRequest('GET', 'api/books');
+~~~
+
+#### `TestCase::assertResponseCode($code)`
+
+| param   | type | description      |
+|---------|------|------------------|
+|`$code`  | int  | HTTP status code |
+
+Checks for a specific response code in your controller tests.
+
+~~~php
+$this->assertResponseCode(200);
+~~~
+
+#### `TestCase::assertRedirect($uri, $code = null)`
+
+| param   | type   | description      |
+|---------|--------|------------------|
+|`$uri`   | string | URI to redirect  |
+|`$code`  | int    | HTTP status code |
+
+Checks if `redirect()` is called in your controller tests.
+
+~~~php
+$this->assertRedirect('auth/login');
+~~~
+
+#### `TestCase::assertResponseHeader($name, $value)`
+
+| param   | type   | description  |
+|---------|--------|--------------|
+|`$name`  | string | header name  |
+|`$value` | string | header value |
+
+Checks for a specific response header in your controller tests.
+
+~~~php
+$this->assertResponseHeader(
+	'Content-Type', 'application/csv; charset=utf-8'
+);
+~~~
+
+**Note:** This method can only assert headers set by `$this->output->set_header()` method.
+
+#### `TestCase::assertResponseCookie($name, $value, $allow_duplicate = false)`
+
+| param             | type         | description                           |
+|-------------------|--------------|---------------------------------------|
+|`$name`            | string       | cookie name                           |
+|`$value`           | string/array | cookie value / array of cookie params |
+|`$allow_duplicate` | bool         | whether to allow duplicated cookies   |
+
+Checks for a specific response cookie in your controller tests.
+
+~~~php
+$this->assertResponseCookie('cookie-name', 'cookie value');
+~~~
+
+You can also check cookie params.
+
+~~~php
+$this->assertResponseCookie(
+	'cookie-name',
+	[
+		'value'  => 'cookie value',
+		'domain' => '.example.com',
+		'path'   => '/',
+		'secure' => TRUE,
+		'httponly' => TRUE,
+	]
+);
+~~~
+
+**Note:** This method can only assert cookies set by `$this->input->set_cooke()` method.
+
+#### `TestCase::getDouble($classname, $params, $enable_constructor = false)`
+
+| param               | type    | description                   |
+|---------------------|---------|-------------------------------|
+|`$classname`         | string  | class name                    |
+|`$params`            | array   | [method_name => return_value] |
+|`$enable_constructor`| bool    | enable constructor or not     |
+
+`returns` (object) PHPUnit mock object
+
+Gets PHPUnit mock object.
+
+~~~php
+$email = $this->getMockBuilder('CI_Email')
+	->disableOriginalConstructor()
+	->setMethods(['send'])
+	->getMock();
+$email->method('send')
+	->willReturn(TRUE);
+~~~
+
+You could write code above like below:
+
+~~~php
+$email = $this->getDouble('CI_Email', ['send' => TRUE]);
+~~~
+
+**Upgrade Note for v0.10.0**
+
+v0.10.0 has changed the default behavior of `$this->getDouble()` and disabled original constructor. If the change causes errors, update your test code like below:
+
+*before:*
+~~~php
+$validation = $this->getDouble('CI_Form_validation', ['run' => TRUE]);
+~~~
+
+↓
+
+*after:*
+~~~php
+$validation = $this->getDouble('CI_Form_validation', ['run' => TRUE], TRUE);
+~~~
+
+#### `TestCase::verifyInvoked($mock, $method, $params)`
+
+| param   | type   | description         |
+|---------|--------|---------------------|
+|`$mock`  | object | PHPUnit mock object |
+|`$method`| string | method name         |
+|`$params`| array  | arguments           |
+
+Verifies a method was invoked at least once.
+
+~~~php
+$loader->expects($this->atLeastOnce())
+	->method('view')
+	->with(
+		['shop_confirm', $this->anything(), TRUE]
+	);
+~~~
+
+You could write code above like below:
+
+~~~php
+$this->verifyInvoked(
+	$loader,
+	'view',
+	[
+		['shop_confirm', $this->anything(), TRUE]
+	]
+);
+~~~
+
+#### `TestCase::verifyInvokedOnce($mock, $method, $params)`
+
+| param   | type   | description         |
+|---------|--------|---------------------|
+|`$mock`  | object | PHPUnit mock object |
+|`$method`| string | method name         |
+|`$params`| array  | arguments           |
+
+Verifies that method was invoked only once.
+
+~~~php
+$loader->expects($this->once())
+	->method('view')
+	->with(
+		['shop_confirm', $this->anything(), TRUE]
+	);
+~~~
+
+You could write code above like below:
+
+~~~php
+$this->verifyInvokedOnce(
+	$loader,
+	'view',
+	[
+		['shop_confirm', $this->anything(), TRUE]
+	]
+);
+~~~
+
+#### `TestCase::verifyInvokedMultipleTimes($mock, $method, $times, $params)`
+
+| param   | type   | description         |
+|---------|--------|---------------------|
+|`$mock`  | object | PHPUnit mock object |
+|`$method`| string | method name         |
+|`$times` | int    | times               |
+|`$params`| array  | arguments           |
+
+Verifies that method was called exactly $times times.
+
+~~~php
+$loader->expects($this->exactly(2))
+	->method('view')
+	->withConsecutive(
+		['shop_confirm', $this->anything(), TRUE],
+		['shop_tmpl_checkout', $this->anything()]
+	);
+~~~
+
+You could write code above like below:
+
+~~~php
+$this->verifyInvokedMultipleTimes(
+	$loader,
+	'view',
+	2,
+	[
+		['shop_confirm', $this->anything(), TRUE],
+		['shop_tmpl_checkout', $this->anything()]
+	]
+);
+~~~
+
+#### `TestCase::verifyNeverInvoked($mock, $method, $params)`
+
+| param   | type   | description         |
+|---------|--------|---------------------|
+|`$mock`  | object | PHPUnit mock object |
+|`$method`| string | method name         |
+|`$params`| array  | arguments           |
+
+Verifies that method was not called.
+
+~~~php
+$loader->expects($this->never())
+	->method('view')
+	->with(
+		['shop_confirm', $this->anything(), TRUE]
+	);
+~~~
+
+You could write code above like below:
+
+~~~php
+$this->verifyNeverInvoked(
+	$loader,
+	'view',
+	[
+		['shop_confirm', $this->anything(), TRUE]
+	]
+);
+~~~
+
+#### `TestCase::warningOff()`
+
+Turns off WARNING and Notice in PHP error reporting.
+
+~~~php
+$this->warningOff();
+$output = $this->request('GET', 'api/example/users');
+$this->warningOn();
+~~~
+
+#### `TestCase::warningOn()`
+
+Restores PHP error reporting.
+
+~~~php
+$this->warningOn();
+~~~
+
+### *class* ReflectionHelper
+
+This class provides helper methods to access private or protected properties and methods.
+
+But generally it is not recommended to test non-public properties or methods, so think twice before you use methods in this class.
+
+#### ReflectionHelper::getPrivateProperty($obj, $property)
+
+| param     | type          | description         |
+|-----------|---------------|---------------------|
+|`$obj`     | object/string | object / class name |
+|`$property`| string        | property name       |
+
+`returns` (mixed) property value
+
+Gets private or protected property value.
+
+~~~php
+$obj = new SomeClass();
+$private_propery = ReflectionHelper::getPrivateProperty(
+	$obj,
+	'private_propery',
+);
+~~~
+
+#### ReflectionHelper::setPrivateProperty($obj, $property, $value)
+
+| param     | type          | description         |
+|-----------|---------------|---------------------|
+|`$obj`     | object/string | object / class name |
+|`$property`| string        | property name       |
+|`$value`   | mixed         | value               |
+
+Sets private or protected property value.
+
+~~~php
+$obj = new SomeClass();
+ReflectionHelper::setPrivateProperty(
+	$obj,
+	'private_propery',
+	'new value'
+);
+~~~
+
+#### ReflectionHelper::getPrivateMethodInvoker($obj, $method)
+
+| param   | type          | description         |
+|---------|---------------|---------------------|
+|`$obj`   | object/string | object / class name |
+|`$method`| string        | method name         |
+
+`returns` (closure) method invoker
+
+Gets private or protected method invoker.
+
+~~~php
+$obj = new SomeClass();
+$method = ReflectionHelper::getPrivateMethodInvoker(
+	$obj, 'privateMethod'
+);
+$this->assertEquals(
+	'return value of the privateMethod() method', $method()
+);
+~~~
+
+### *class* MonkeyPatch
+
+To use this class, you have to enable monkey patching. See [How to Write Tests](HowToWriteTests.md#monkey-patching).
+
+#### `MonkeyPatch::patchFunction($function, $return_value, $class_method)`
+
+| param         | type   | description                                    |
+|---------------|--------|------------------------------------------------|
+|`$function`    | string | function name to patch                         |
+|`$return_value`| mixed  | return value / callback                        |
+|`$class_method`| string | class::method or classname to apply this patch |
+
+Replaces function on the fly.
+
+If `$class_method` is present, the patch is applied to the functions only in the class method or in the class.
+
+There are some known limitations. See [How to Write Tests](HowToWriteTests.md#patching-functions) for details.
+
+~~~php
+MonkeyPatch::patchFunction('mt_rand', 100, 'Welcome::index');
+~~~
+
+#### `MonkeyPatch::resetFunctions()`
+
+Resets all patched functions.
+
+This method is called on `TestCase::tearDown()` by default. So you don't have to call it normally.
+
+#### `MonkeyPatch::patchMethod($classname, $params)`
+
+| param       | type   | description                   |
+|-------------|--------|-------------------------------|
+|`$classname` | string | class name to patch           |
+|`$params`    | array  | [method_name => return_value] |
+
+Replaces method in user-defined class on the fly.
+
+~~~php
+MonkeyPatch::patchMethod(
+	'Category_model',
+	['get_category_list' => [(object) ['name' => 'Nothing']]]
+);
+~~~
+
+#### `MonkeyPatch::resetMethods()`
+
+Resets all patched class methods.
+
+This method is called on `TestCase::tearDown()` by default. So you don't have to call it normally.
+
+#### `MonkeyPatch::patchConstant($constant, $value, $class_method)`
+
+| param         | type   | description                                    |
+|---------------|--------|------------------------------------------------|
+|`$constant`    | string | constant name to patch                         |
+|`$value`       | mixed  | value                                          |
+|`$class_method`| string | class::method or classname to apply this patch |
+
+Replaces constant value on the fly.
+
+If `$class_method` is present, the patch is applied to the constants only in the class method or in the class.
+
+There are some known limitations. See [How to Write Tests](HowToWriteTests.md#patching-constants) for details.
+
+~~~php
+MonkeyPatch::patchConstant('ENVIRONMENT', 'development', 'Welcome::index');
+~~~
+
+#### `MonkeyPatch::resetConstants()`
+
+Resets all patched constants.
+
+This method is called on `TestCase::tearDown()` by default. So you don't have to call it normally.
+
+#### `MonkeyPatch::verifyInvoked($class_method, $params)`
+
+| param         | type   | description              |
+|---------------|--------|--------------------------|
+|`$class_method`| string | class::method / function |
+|`$params`      | array  | arguments                |
+
+Verifies a patched class method or a patched function was invoked at least once.
+
+~~~php
+MonkeyPatch::verifyInvoked(
+	'Ion_auth_model::login', ['foo', 'bar']
+);
+~~~
+
+#### `MonkeyPatch::verifyInvokedOnce($class_method, $params)`
+
+| param         | type   | description              |
+|---------------|--------|--------------------------|
+|`$class_method`| string | class::method / function |
+|`$params`      | array  | arguments                |
+
+Verifies that patched class method or a patched function was invoked only once.
+
+~~~php
+MonkeyPatch::verifyInvokedOnce(
+	'CI_Input::post', ['id']
+);
+~~~
+
+#### `MonkeyPatch::verifyInvokedMultipleTimes($class_method, $times, $params)`
+
+| param         | type   | description              |
+|---------------|--------|--------------------------|
+|`$class_method`| string | class::method / function |
+|`$times`       | int    | times                    |
+|`$params`      | array  | arguments                |
+
+Verifies that patched method or a patched function was called exactly $times times.
+
+~~~php
+MonkeyPatch::verifyInvokedMultipleTimes(
+	'CI_Input::post', 2
+);
+~~~
+
+#### `MonkeyPatch::verifyNeverInvoked($class_method, $params)`
+
+| param         | type   | description              |
+|---------------|--------|--------------------------|
+|`$class_method`| string | class::method / function |
+|`$params`      | array  | arguments                |
+
+Verifies that patched method or a patched function was not called.
+
+~~~php
+MonkeyPatch::verifyNeverInvoked(
+	'Ion_auth_model::login', ['username', 'PHS/DL1m6OMYg']
+);
+~~~
diff --git a/vendor/kenjis/ci-phpunit-test/docs/HowToWriteTests.md b/vendor/kenjis/ci-phpunit-test/docs/HowToWriteTests.md
new file mode 100644
index 00000000000..755cefb6931
--- /dev/null
+++ b/vendor/kenjis/ci-phpunit-test/docs/HowToWriteTests.md
@@ -0,0 +1,1107 @@
+# ci-phpunit-test for CodeIgniter 3.0
+
+version: **v0.12.1** | 
+[v0.11.3](https://github.com/kenjis/ci-phpunit-test/blob/v0.11.3/docs/HowToWriteTests.md) | 
+[v0.10.1](https://github.com/kenjis/ci-phpunit-test/blob/v0.10.1/docs/HowToWriteTests.md) | 
+[v0.9.1](https://github.com/kenjis/ci-phpunit-test/blob/v0.9.1/docs/HowToWriteTests.md) | 
+[v0.8.2](https://github.com/kenjis/ci-phpunit-test/blob/v0.8.2/docs/HowToWriteTests.md) | 
+[v0.7.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.7.0/docs/HowToWriteTests.md) | 
+[v0.6.2](https://github.com/kenjis/ci-phpunit-test/blob/v0.6.2/docs/HowToWriteTests.md) | 
+[v0.5.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.5.0/docs/HowToWriteTests.md) | 
+[v0.4.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.4.0/docs/HowToWriteTests.md) | 
+[v0.3.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.3.0/docs/HowToWriteTests.md) | 
+[v0.2.0](https://github.com/kenjis/ci-phpunit-test/blob/v0.2.0/docs/HowToWriteTests.md)
+
+## How to Write Tests
+
+- [Introduction](#introduction)
+- [Testing Environment](#testing-environment)
+- [Can and Can't](#can-and-cant)
+	- [MY_Loader](#my_loader)
+	- [MY_Input](#my_input)
+	- [`exit()`](#exit)
+	- [Reset CodeIgniter object](#reset-codeigniter-object)
+	- [Hooks](#hooks)
+	- [Autoloader](#autoloader)
+- [Basic Conventions](#basic-conventions)
+- [Models](#models)
+	- [Using Database](#using-database)
+	- [Database Seeding](#database-seeding)
+	- [Using PHPUnit Mock Objects](#using-phpunit-mock-objects)
+- [Libraries](#libraries)
+- [Controllers](#controllers)
+	- [Request to Controller](#request-to-controller)
+	- [REST Request](#rest-request)
+	- [Ajax Request](#ajax-request)
+	- [Request and Use Mocks](#request-and-use-mocks)
+	- [Request and Use Monkey Patching](#request-and-use-monkey-patching)
+	- [Check Status Code](#check-status-code)
+	- [Examine DOM in Controller Output](#examine-dom-in-controller-output)
+	- [Controller with Authentication](#controller-with-authentication)
+	- [`redirect()`](#redirect)
+	- [`show_error()` and `show_404()`](#show_error-and-show_404)
+	- [Session](#session)
+	- [Controller with Hooks](#controller-with-hooks)
+	- [Controller with Name Collision](#controller-with-name-collision)
+- [Mock Libraries](#mock-libraries)
+- [Monkey Patching](#monkey-patching)
+	- [Converting `exit()` to Exception](#converting-exit-to-exception)
+	- [Patching Functions](#patching-functions)
+	- [Patching Methods in User-defined Classes](#patching-methods-in-user-defined-classes)
+	- [Patching Constants](#patching-constants)
+- [More Samples](#more-samples)
+- [Third Party Libraries](#third-party-libraries)
+	- [CodeIgniter Rest Server](#codeigniter-rest-server)
+	- [Modular Extensions - HMVC](#modular-extensions---hmvc)
+
+### Introduction
+
+Here is my advice:
+
+* You don't have to write your business logic in your controllers. Write them in your models.
+* You should test models first, and test them well.
+
+And PHPUnit has great documentation. You should read [Writing Tests for PHPUnit](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html).
+
+If you are not familiar with *testing*, I recommend you read my book, *[CodeIgniter Testing Guide](https://leanpub.com/codeigniter-testing-guide)*. It is a beginners' guide to automated testing in PHP.
+
+### Testing Environment
+
+Tests always run on `testing` environment.
+
+If you don't know well about config files and environments, see [CodeIgniter User Guide](http://www.codeigniter.com/user_guide/libraries/config.html#environments).
+
+### Can and Can't
+
+ci-phpunit-test does not want to modify CodeIgniter files. The more you modify them, the more you get difficulties when you update CodeIgniter.
+
+In fact, it uses modified classes and some functions. But I try to modify as little as possible.
+
+The core functions and classes which are modified:
+
+* function `get_instance()`
+* function `load_class()`
+* function `is_loaded()`
+* function `get_config()`
+* function `config_item()`
+* function `is_cli()`
+* function `show_error()`
+* function `show_404()`
+* function `set_status_header()`
+* class `CI_Loader`
+* class `CI_Input`
+
+and a helper which is modified:
+
+* function `redirect()` in URL helper
+
+All of them are placed in `tests/_ci_phpunit_test/replacing` folder.
+
+And ci-phpunit-test adds properties dynamically:
+
+* property `CI_Output::_status`
+* property `CI_Output::_cookies`
+
+And ci-phpunit-test has a modified bootstrap file:
+
+* `core/CodeIgniter.php`
+
+**Note to Maintainer:** If you modify another CodeIgniter file, update `bin/check-diff.sh` and `bin/check-ci-diff.sh`, too.
+
+#### MY_Loader
+
+ci-phpunit-test replaces `CI_Loader` and modifies below methods:
+
+* `CI_Loader::model()`
+* `CI_Loader::_ci_load_library()`
+* `CI_Loader::_ci_load_stock_library()`
+
+But if you place MY_Loader, your MY_Loader extends the loader of ci-phpunit-test.
+
+If your MY_Loader overrides the above methods, you have to take care of changes in the loader of ci-phpunit-test.
+
+#### MY_Input
+
+ci-phpunit-test replaces `CI_Input` and modifies below method:
+
+* `CI_Input::set_cookie()`
+* `CI_Input::get_request_header()`
+
+But if you place MY_Input, your MY_Input extends the CI_Input of ci-phpunit-test.
+
+If your MY_Input overrides the above method, you have to take care of changes in the CI_Input of ci-phpunit-test.
+
+#### `exit()`
+
+When a test exercises code that contains `exit()` or `die()` statement, the execution of the whole test suite is aborted.
+
+For example, if you write `exit()` in your controller code, your testing ends with it.
+
+I recommend you not using `exit()` or `die()` in your code.
+
+**Monkey Patching on `exit()`**
+
+ci-phpunit-test has functionality that makes all `exit()` and `die()` in your code throw `CIPHPUnitTestExitException`.
+
+See [Monkey Patching](#monkey-patching) for details.
+
+**`show_error()` and `show_404()`**
+
+And ci-phpunit-test has special [show_error() and show_404()](#show_error-and-show_404).
+
+**`redirect()`**
+
+ci-phpunit-test replaces `redirect()` function in URL helper. Using it, you can easily test controllers that contain `redirect()`. See [redirect()](#redirect) for details.
+
+#### Reset CodeIgniter object
+
+CodeIgniter has a function `get_instance()` to get the CodeIgniter object (CodeIgniter instance or CodeIgniter super object).
+
+ci-phpunit-test has a new function [reset_instance()](FunctionAndClassReference.md#function-reset_instance) which reset the current CodeIgniter object. After resetting, you can (and must) create a new your Controller instance with new state.
+
+#### Hooks
+
+If you enable CodeIgniter's hooks, hook `pre_system` is called once in PHPUnit bootstrap.
+
+If you use `$this->request->enableHooks()` and `$this->request()`, hook `pre_controller`, `post_controller_constructor`, `post_controller` and `display_override` are called on every `$this->request()` to a controller.
+
+See [Controller with Hooks](#controller-with-hooks) for details.
+
+#### Autoloader
+
+ci-phpunit-test has an autoloader for class files.
+
+To change the search paths, change the line [`CIPHPUnitTest::init();`](https://github.com/kenjis/ci-phpunit-test/blob/v0.12.1/application/tests/Bootstrap.php#L336) in `tests/Bootstrap.php` like below:
+
+~~~php
+CIPHPUnitTest::init([
+	// Directories for autoloading
+	APPPATH.'models',
+	APPPATH.'libraries',
+	APPPATH.'controllers',
+	APPPATH.'modules',
+]);
+~~~
+
+You must put all directories to search class files in the array.
+
+### Basic Conventions
+
+1. The tests for a class `Class` go into a class `Class_test`.
+2. `Class_test` inherits from [TestCase](FunctionAndClassReference.md#class-testcase) class in ci-phpunit-test.
+3. The tests are public methods that are named `test_*`. (Or you can use the `@test` annotation in a method's docblock to mark it as a test method.)
+
+* Don't forget to write `parent::setUpBeforeClass();` if you override `setUpBeforeClass()` method.
+* Don't forget to write `parent::tearDown();` if you override `tearDown()` method.
+
+*tests/libraries/Foo_test.php*
+~~~php
+class Foo_test extends TestCase
+{
+	public function setUp()
+	{
+		$this->resetInstance();
+		$this->CI->load->library('Foo');
+		$this->obj = $this->CI->foo;
+	}
+
+	public function test_doSomething()
+	{
+		$actual = $this->obj->doSomething();
+		$expected = 'something';
+		$this->assertEquals($expected, $actual);
+	}
+}
+~~~
+
+[$this->resetInstance()](FunctionAndClassReference.md#testcaseresetinstance) method in ci-phpunit-test is a helper method to reset CodeIgniter instance and assign new CodeIgniter instance as `$this->CI`.
+
+### Models
+
+#### Using Database
+
+*tests/models/Inventory_model_test.php*
+~~~php
+resetInstance();
+		$this->CI->load->model('shop/Inventory_model');
+		$this->obj = $this->CI->Inventory_model;
+	}
+
+	public function test_get_category_list()
+	{
+		$expected = [
+			1 => 'Book',
+			2 => 'CD',
+			3 => 'DVD',
+		];
+		$list = $this->obj->get_category_list();
+		foreach ($list as $category) {
+			$this->assertEquals($expected[$category->id], $category->name);
+		}
+	}
+
+	public function test_get_category_name()
+	{
+		$actual = $this->obj->get_category_name(1);
+		$expected = 'Book';
+		$this->assertEquals($expected, $actual);
+	}
+}
+~~~
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/models/Category_model_test.php).
+
+#### Database Seeding
+
+I put [Seeder Library](../application/libraries/Seeder.php) and a sample [Seeder File](../application/database/seeds/CategorySeeder.php).
+
+They are not installed, so if you want to use, copy them manually.
+
+You can use them like below:
+
+~~~php
+	public static function setUpBeforeClass()
+	{
+		parent::setUpBeforeClass();
+
+		$CI =& get_instance();
+		$CI->load->library('Seeder');
+		$CI->seeder->call('CategorySeeder');
+	}
+~~~
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/models/Category_model_test.php).
+
+#### Using PHPUnit Mock Objects
+
+You can use `$this->getMockBuilder()` method in PHPUnit and [$this->verifyInvoked*()](FunctionAndClassReference.md#testcaseverifyinvokedmock-method-params) helper method in ci-phpunit-test.
+
+If you don't know well about PHPUnit Mock Objects, see [Test Doubles](https://phpunit.de/manual/current/en/test-doubles.html).
+
+~~~php
+	public function setUp()
+	{
+		$this->resetInstance();
+		$this->CI->load->model('Category_model');
+		$this->obj = $this->CI->Category_model;
+	}
+
+	public function test_get_category_list()
+	{
+		// Create mock objects for CI_DB_pdo_result and CI_DB_pdo_sqlite_driver
+		$return = [
+			0 => (object) ['id' => '1', 'name' => 'Book'],
+			1 => (object) ['id' => '2', 'name' => 'CD'],
+			2 => (object) ['id' => '3', 'name' => 'DVD'],
+		];
+		$db_result = $this->getMockBuilder('CI_DB_pdo_result')
+			->disableOriginalConstructor()
+			->getMock();
+		$db_result->method('result')->willReturn($return);
+		$db = $this->getMockBuilder('CI_DB_pdo_sqlite_driver')
+			->disableOriginalConstructor()
+			->getMock();
+		$db->method('get')->willReturn($db_result);
+
+		// Verify invocations
+		$this->verifyInvokedOnce(
+			$db_result,
+			'result',
+			[]
+		);
+		$this->verifyInvokedOnce(
+			$db,
+			'order_by',
+			['id']
+		);
+		$this->verifyInvokedOnce(
+			$db,
+			'get',
+			['category']
+		);
+
+		// Replace property db with mock object
+		$this->obj->db = $db;
+
+		$expected = [
+			1 => 'Book',
+			2 => 'CD',
+			3 => 'DVD',
+		];
+		$list = $this->obj->get_category_list();
+		foreach ($list as $category) {
+			$this->assertEquals($expected[$category->id], $category->name);
+		}
+	}
+~~~
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/models/Category_model_mocking_db_test.php).
+
+### Libraries
+
+If your library depends on CodeIgniter functionality, I recommend using `setUp()` method like this:
+
+~~~php
+	public function setUp()
+	{
+		$this->resetInstance();
+		$this->CI->load->library('Someclass');
+		$this->obj = $this->CI->someclass;
+	}
+~~~
+
+If your library is decoupled from CodeIgniter functionality, you can use `setUp()` method like this:
+
+~~~php
+	public function setUp()
+	{
+		$this->obj = new Someclass();
+	}
+~~~
+
+In this case, ci-phpunit-test autoloads your libraries in `application/libraries` folder.
+
+### Controllers
+
+#### Request to Controller
+
+You can use [$this->request()](FunctionAndClassReference.md#testcaserequestmethod-argv-params--) method in ci-phpunit-test.
+
+~~~php
+	public function test_uri_sub_sub_index()
+	{
+		$output = $this->request('GET', 'sub/sub/index');
+		$this->assertContains('Page Title', $output);
+	}
+~~~
+
+**Note:** If you pass URI string to the 2nd argument of `$this->request()`, it invokes the routing. If the resolved controller has `_remap()` and/or `_output()` methods, they will be invoked, too.
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/sub/Sub_test.php).
+
+If you want to call a controller method directly, you can pass an array to the 2nd argument of `$this->request()`.
+
+*tests/controllers/Welcome_test.php*
+~~~php
+request('GET', ['Welcome', 'index']);
+		$this->assertContains('Welcome to CodeIgniter', $output);
+	}
+}
+~~~
+
+**Note:** If you pass an array to the 2nd argument of `$this->request()`, it does not invokes the routing. The `_remap()` and/or `_output()` methods in a controller are not invoked, too.
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Welcome_test.php).
+
+#### REST Request
+
+You can specify request method in 2nd argument of [$this->request()](FunctionAndClassReference.md#testcaserequestmethod-argv-params--) method and request body in 3rd argument of `$this->request()`.
+
+~~~php
+		$output = $this->request(
+			'PUT', 'api/user', json_encode(['name' => 'mike'])
+		);
+~~~
+
+~~~php
+		$output = $this->request(
+			'DELETE', 'api/key', 'key=12345678'
+		);
+~~~
+
+You can set request header with [$this->request->setHeader()](FunctionAndClassReference.md#request-setheader) method in ci-phpunit-test. And you can confirm response header with [$this->assertResponseHeader()](FunctionAndClassReference.md#testcaseassertresponseheadername-value) method in ci-phpunit-test.
+
+~~~php
+	public function test_users_get_id_with_http_accept_header()
+	{
+		$this->request->setHeader('Accept', 'application/csv');
+		$output = $this->request('GET', 'api/example/users/id/1');
+		$this->assertEquals(
+			'id,name,email,fact
+1,John,john@example.com,"Loves coding"
+',
+			$output
+		);
+		$this->assertResponseCode(200);
+		$this->assertResponseHeader(
+			'Content-Type', 'application/csv; charset=utf-8'
+		);
+	}
+~~~
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/api/Example_test.php).
+
+#### Ajax Request
+
+You can use [$this->ajaxRequest()](FunctionAndClassReference.md#testcaseajaxrequestmethod-argv-params--) method in ci-phpunit-test.
+
+~~~php
+	public function test_index_ajax_call()
+	{
+		$output = $this->ajaxRequest('GET', 'ajax/index');
+		$expected = '{"name":"John Smith","age":33}';
+		$this->assertEquals($expected, $output);
+	}
+~~~
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Ajax_test.php).
+
+#### Request and Use Mocks
+
+You can use [$this->request->setCallable()](FunctionAndClassReference.md#request-setcallable) method in ci-phpunit-test. [$this->getDouble()](FunctionAndClassReference.md#testcasegetdoubleclassname-params-enable_constructor--false) is a helper method in ci-phpunit-test.
+
+~~~php
+	public function test_send_okay()
+	{
+		$this->request->setCallable(
+			function ($CI) {
+				$email = $this->getDouble('CI_Email', ['send' => TRUE]);
+				$CI->email = $email;
+			}
+		);
+		$output = $this->request(
+			'POST',
+			['Contact', 'send'],
+			[
+				'name' => 'Mike Smith',
+				'email' => 'mike@example.jp',
+				'body' => 'This is test mail.',
+			]
+		);
+		$this->assertContains('Mail sent', $output);
+	}
+~~~
+
+**Note:** When you have not loaded a class with CodeIgniter loader, if you make a mock object for the class, your application code may not work correclty. If you have got an error, please try to load it with CodeIgniter loader, before getting the mock object.
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Mock_phpunit_test.php).
+
+The function you set by `$this->request->setCallable()` runs after controller instantiation. So you can't inject mocks into controller constructor.
+
+##### Inject Mocks into Controller Constructors
+
+For example, if you have a controller like this:
+
+~~~php
+class Auth extends CI_Controller
+{
+	public function __construct()
+	{
+		parent::__construct();
+
+		$this->load->library('ion_auth');
+		if ( ! $this->ion_auth->logged_in())
+		{
+			$this->load->helper('url');
+			redirect('auth/login');
+		}
+	}
+
+	...
+}
+~~~
+
+In this case, You can use [$this->request->setCallablePreConstructor()](FunctionAndClassReference.md#request-setcallablepreconstructor) method and [load_class_instance()](FunctionAndClassReference.md#function-load_class_instanceclassname-instance) function in ci-phpunit-test.
+
+**Note:** Unlike `$this->request->setCallable()`, this callback runs before the controller is created. So there is no CodeIgniter instance yet. You can't use CodeIgniter objects.
+
+~~~php
+	public function test_index_logged_in()
+	{
+		$this->request->setCallablePreConstructor(
+			function () {
+				// Get mock object
+				$auth = $this->getDouble(
+					'Ion_auth', ['logged_in' => TRUE]
+				);
+				// Inject mock object
+				load_class_instance('ion_auth', $auth);
+			}
+		);
+
+		$output = $this->request('GET', 'auth/login');
+		$this->assertContains('You are logged in.', $output);
+	}
+~~~
+
+See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Auth_check_in_construct_test.php).
+
+**Note:** If you can't create mocks or it is too hard to create mocks, it may be better to use Monkey Patching.
+
+#### Request and Use Monkey Patching
+
+To use Monkey Patching, you have to enable it. See [Monkey Patching](#monkey-patching).
+
+~~~php
+	public function test_index_logged_in()
+	{
+		MonkeyPatch::patchMethod('Ion_auth', ['logged_in' => TRUE]);
+
+		$output = $this->request('GET', 'auth/login');
+		$this->assertContains('You are logged in.', $output);
+	}
+~~~
+
+See also [Patching Methods in User-defined Classes](#patching-methods-in-user-defined-classes).
+
+#### Check Status Code
+
+You can use [$this->assertResponseCode()](FunctionAndClassReference.md#testcaseassertresponsecodecode) method in ci-phpunit-test.
+
+~~~php
+		$this->request('GET', 'welcome');
+		$this->assertResponseCode(200);
+~~~
+
+#### Examine DOM in Controller Output
+
+I recommend using [symfony/dom-crawler](http://symfony.com/doc/current/components/dom_crawler.html).
+
+~~~php
+		$output = $this->request('GET', ['Welcome', 'index']);
+		$crawler = new Symfony\Component\DomCrawler\Crawler($output);
+		// Get the text of the first 

+ $text = $crawler->filter('h1')->eq(0)->text(); +~~~ + +See [working sample](https://github.com/kenjis/codeigniter-tettei-apps/blob/develop/application/tests/controllers/Bbs_test.php#L126-128). + +#### Controller with Authentication + +I recommend using PHPUnit mock objects. [$this->getDouble()](FunctionAndClassReference.md#testcasegetdoubleclassname-params-enable_constructor--false) is a helper method in ci-phpunit-test. + +~~~php + public function test_index_logged_in() + { + $this->request->setCallable( + function ($CI) { + // Get mock object + $auth = $this->getDouble( + 'Ion_auth', ['logged_in' => TRUE, 'is_admin' => TRUE] + ); + // Inject mock object + $CI->ion_auth = $auth; + } + ); + $output = $this->request('GET', ['Auth', 'index']); + $this->assertContains('

Below is a list of the users.

', $output); + } +~~~ + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Auth_test.php). + +#### `redirect()` + +By default, ci-phpunit-test replaces `redirect()` function in URL helper. Using it, you can easily test controllers that contain `redirect()`. + +But you could still override `redirect()` using your `MY_url_helper.php`. If you place `MY_url_helper.php`, your `redirect()` will be used. + +If you use `redirect()` in ci-phpunit-test, you can write tests like this: + +~~~php + public function test_index() + { + $this->request('GET', ['Admin', 'index']); + $this->assertRedirect('login', 302); + } +~~~ + +[$this->assertRedirect()](FunctionAndClassReference.md#testcaseassertredirecturi-code--null) is a method in ci-phpunit-test. + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Redirect_test.php). + +##### Upgrade Note for v0.4.0 + +v0.4.0 has new `MY_url_helper.php`. If you use it, you must update your tests. + +*before:* +~~~php + /** + * @expectedException PHPUnit_Framework_Exception + * @expectedExceptionCode 302 + * @expectedExceptionMessageRegExp !\ARedirect to http://localhost/\z! + */ + public function test_index() + { + $this->request('GET', ['Redirect', 'index']); + } +~~~ + +↓ + +*after:* +~~~php + public function test_index() + { + $this->request('GET', ['Redirect', 'index']); + $this->assertRedirect('/', 302); + } +~~~ + +#### `show_error()` and `show_404()` + +You can use [$this->assertResponseCode()](FunctionAndClassReference.md#testcaseassertresponsecodecode) method in ci-phpunit-test. + +~~~php + public function test_index() + { + $this->request('GET', ['nocontroller', 'noaction']); + $this->assertResponseCode(404); + } +~~~ + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Nocontroller_test.php). + +If you don't call `$this->request()` in your tests, `show_error()` throws `CIPHPUnitTestShowErrorException` and `show_404()` throws `CIPHPUnitTestShow404Exception`. So you must expect the exceptions. You can use `@expectedException` annotation in PHPUnit. + +##### Upgrade Note for v0.4.0 + +v0.4.0 has changed how to test `show_error()` and `show_404()`. You must update your tests. + +*before:* +~~~php + /** + * @expectedException PHPUnit_Framework_Exception + * @expectedExceptionCode 404 + */ + public function test_index() + { + $this->request('GET', 'show404'); + } +~~~ + +↓ + +*after:* +~~~php + public function test_index() + { + $this->request('GET', 'show404'); + $this->assertResponseCode(404); + } +~~~ + +#### Session + +If you run CodeIgniter via CLI, CodeIgniter's Session class does not call `session_start()`. So normally you don't see warning like "session_start(): Cannot send session cookie - headers already sent by ...". + +But if libraries which you use have logic runs only when not in CLI mode, you have to use `set_is_cli(FALSE)` for testing. (Don't forget calling `set_is_cli(TRUE)` after running the code.) + +In that case, Session class calls `session_start()` and you will see "Cannot send session cookie" warning. + +To test that code, you can add `$this->warningOff()` to your test code (don't forget calling `$this->warningOn()` after running the code), or you can use *MY_Session* class like this: [application/libraries/Session/MY_Session.php](../application/libraries/Session/MY_Session.php). + +#### Controller with Hooks + +If you want to enable hooks, call [$this->request->enableHooks()](FunctionAndClassReference.md#request-enablehooks) method. It enables `pre_controller`, `post_controller_constructor`, `post_controller` and `display_override` hooks. + +~~~php + $this->request->enableHooks(); + $output = $this->request('GET', 'products/shoes/show/123'); +~~~ + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Hook_test.php). + +#### Controller with Name Collision + +If you have two controllers with the exact same name, PHP Fatal error stops PHPUnit testing. + +In this case, you can use PHPUnit annotations `@runInSeparateProcess` and `@preserveGlobalState disabled`. But tests in a separate PHP process are very slow. + +*tests/controllers/sub/Welcome_test.php* +~~~php +request('GET', 'sub/welcome/index'); + $this->assertContains('Page Title', $output); + } +} +~~~ + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/sub/Welcome_test.php). + +### Mock Libraries + +You can put mock libraries in `tests/mocks/libraries` folder. You can see [application/tests/mocks/libraries/email.php](../application/tests/mocks/libraries/email.php) as a sample. + +With mock libraries, you could replace your object in CodeIgniter instance. + +This is how to replace Email library with `Mock_Libraries_Email` class. + +~~~php + public function setUp() + { + $this->resetInstance(); + $this->CI->load->model('Mail_model'); + $this->obj = $this->CI->Mail_model; + $this->CI->email = new Mock_Libraries_Email(); + } +~~~ + +Mock library class name must be `Mock_Libraries_*`, and it is autoloaded. + +### Monkey Patching + +ci-phpunit-test has three monkey patchers. + +* `ExitPatcher`: Converting `exit()` to Exception +* `FunctionPatcher`: Patching Functions +* `MethodPatcher`: Patching Methods in User-defined Classes +* `ConstantPatcher`: Changing Constant Values + +**Note:** This functionality has a negative impact on speed of tests. + +To enable monkey patching, uncomment below code in `tests/Bootstrap.php` and configure them: + +~~~php +/* +require __DIR__ . '/_ci_phpunit_test/patcher/bootstrap.php'; +MonkeyPatchManager::init([ + // PHP Parser: PREFER_PHP7, PREFER_PHP5, ONLY_PHP7, ONLY_PHP5 + 'php_parser' => 'PREFER_PHP5', + 'cache_dir' => APPPATH . 'tests/_ci_phpunit_test/tmp/cache', + // Directories to patch on source files + 'include_paths' => [ + APPPATH, + BASEPATH, + APPPATH . 'tests/_ci_phpunit_test/replacing/', + ], + // Excluding directories to patch + 'exclude_paths' => [ + APPPATH . 'tests/', + '-' . APPPATH . 'tests/_ci_phpunit_test/replacing/', + ], + // All patchers you use. + 'patcher_list' => [ + 'ExitPatcher', + 'FunctionPatcher', + 'MethodPatcher', + 'ConstantPatcher', + ], + // Additional functions to patch + 'functions_to_patch' => [ + //'random_string', + ], + 'exit_exception_classname' => 'CIPHPUnitTestExitException', +]); +*/ +~~~ + +##### Upgrade Note for v0.11.0 + +Add the below line in `include_paths`. + +~~~php + APPPATH . 'tests/_ci_phpunit_test/replacing/', +~~~ + +And add the below line in `exclude_paths`. + +~~~php + '-' . APPPATH . 'tests/_ci_phpunit_test/replacing/', +~~~ + +You can add the parser preference with `php_parser`. The default is `PREFER_PHP5`. Change the config if you need. + +~~~php + // PHP Parser: PREFER_PHP7, PREFER_PHP5, ONLY_PHP7, ONLY_PHP5 + 'php_parser' => 'PREFER_PHP5', +~~~ + +##### Upgrade Note for v0.6.0 + +Add the above code (`require` and `MonkeyPatchManager::init()`) before + +~~~php +/* + * ------------------------------------------------------------------- + * Added for ci-phpunit-test + * ------------------------------------------------------------------- + */ +~~~ + +in [tests/Bootstrap.php](https://github.com/kenjis/ci-phpunit-test/blob/v0.5.0/application/tests/Bootstrap.php). + +`TestCase::$enable_patcher` was removed. Please remove it. + +#### Converting `exit()` to Exception + +This patcher converts `exit()` or `die()` statements to exceptions on the fly. + +If you have a controller like below: + +~~~php + public function index() + { + $this->output + ->set_status_header(200) + ->set_content_type('application/json', 'utf-8') + ->set_output(json_encode(['foo' => 'bar'])) + ->_display(); + exit(); + } +~~~ + +A test case could be like this: + +~~~php + public function test_index() + { + try { + $this->request('GET', 'welcome/index'); + } catch (CIPHPUnitTestExitException $e) { + $output = ob_get_clean(); + } + $this->assertContains('{"foo":"bar"}', $output); + } +~~~ + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Exit_to_exception_test.php). + +#### Patching Functions + +This patcher allows replacement of global functions that can't be mocked by PHPUnit. + +But it has a few limitations. Some functions can't be replaced and it might cause errors. + +So by default we can replace only a dozen pre-defined functions in [FunctionPatcher](https://github.com/kenjis/ci-phpunit-test/blob/v0.12.1/application/tests/_ci_phpunit_test/patcher/Patcher/FunctionPatcher.php#L27). + +~~~php + public function test_index() + { + MonkeyPatch::patchFunction('mt_rand', 100, 'Welcome::index'); + $output = $this->request('GET', 'welcome/index'); + $this->assertContains('100', $output); + } +~~~ + +[MonkeyPatch::patchFunction()](FunctionAndClassReference.md#monkeypatchpatchfunctionfunction-return_value-class_method) replaces PHP native function `mt_rand()` in `Welcome::index` method, and it will return `100` in the test method. + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Patching_on_function_test.php). + +**Note:** If you call `MonkeyPatch::patchFunction()` without 3rd argument, all the functions (located in `include_paths` and not in `exclude_paths`) called in the test method will be replaced. So, for example, a function in CodeIgniter code might be replaced and it results in unexpected outcome. + +**Change Return Value** + +You could change return value of patched function using PHP closure: + +~~~php + MonkeyPatch::patchFunction( + 'function_exists', + function ($function) { + if ($function === 'random_bytes') + { + return true; + } + elseif ($function === 'openssl_random_pseudo_bytes') + { + return false; + } + elseif ($function === 'mcrypt_create_iv') + { + return false; + } + else + { + return __GO_TO_ORIG__; + } + }, + 'Welcome' + ); +~~~ + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Patching_on_function_test.php#L59-L80). + +**Patch on Other Functions** + +If you want to patch other functions, you can add them to [functions_to_patch](https://github.com/kenjis/ci-phpunit-test/blob/v0.12.1/application/tests/Bootstrap.php#L323) in `MonkeyPatchManager::init()`. + +But there are a few known limitations: + +* Patched functions which have parameters called by reference don't work. +* You may see visibility errors if you pass non-public callbacks to patched functions. For example, you pass `[$this, 'method']` to `array_map()` and the `method()` method in the class is not public. + +#### Patching Methods in User-defined Classes + +This patcher allows replacement of methods in user-defined classes. + +~~~php + public function test_index() + { + MonkeyPatch::patchMethod( + 'Category_model', + ['get_category_list' => [(object) ['name' => 'Nothing']]] + ); + $output = $this->request('GET', 'welcome/index'); + $this->assertContains('Nothing', $output); + } +~~~ + +[MonkeyPatch::patchMethod()](FunctionAndClassReference.md#monkeypatchpatchmethodclassname-params) replaces `get_category_list()` method in `Category_model`, and it will return `[(object) ['name' => 'Nothing']]` in the test method. + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/v0.12.1/application/tests/controllers/Patching_on_method_test.php). + +#### Patching Constants + +This patcher allows replacement of constant value. + +~~~php + public function test_index() + { + MonkeyPatch::patchConstant('ENVIRONMENT', 'development', 'Welcome::index'); + $output = $this->request('GET', 'welcome/index'); + $this->assertContains('development', $output); + } +~~~ + +[MonkeyPatch::patchConstant()](FunctionAndClassReference.md#monkeypatchpatchconstantconstant-value-class_method) replaces the return value of the constant `ENVIRONMENT` in `Welcome::index` method. + +There are a few known limitations: + +* Cannot patch constants that are used as default values in function arguments. +* Cannot patch constants that are used as default values in constant declarations. +* Cannot patch constants that are used as default values in property declarations. +* Cannot patch constants that are used as default values in static variable declarations. + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/blob/master/application/tests/controllers/Patching_on_constant_test.php). + +##### Upgrade Note for v0.12.0 + +If you want to use the constant patcher, please add `ConstantPatcher` in the `patcher_list` in [tests/Bootstrap.php](https://github.com/kenjis/ci-phpunit-test/blob/master/application/tests/Bootstrap.php#L340). + +*before:* +~~~php + // All patchers you use. + 'patcher_list' => [ + 'ExitPatcher', + 'FunctionPatcher', + 'MethodPatcher', + ], +~~~ + +↓ + +*after:* +~~~php + // All patchers you use. + 'patcher_list' => [ + 'ExitPatcher', + 'FunctionPatcher', + 'MethodPatcher', + 'ConstantPatcher', // Add this + ], +~~~ + +### More Samples + +Want to see more tests? + +* https://github.com/kenjis/ci-app-for-ci-phpunit-test/tree/v0.12.0/application/tests +* https://github.com/kenjis/codeigniter-tettei-apps/tree/develop/application/tests + +### Third Party Libraries + +ci-phpunit-test has powerful functionality for testing. So normally you don't have to modify your application or library code. + +But there are still libraries which can't be tested without code modification. + +#### [CodeIgniter Rest Server](https://github.com/chriskacerguis/codeigniter-restserver/) + +codeigniter-restserver calls `exit()`. So you have to enable [Monkey Patching](#monkey-patching) and at least you have to use `ExitPatcher`. + +Additionally you have to apply patch on `application/libraries/REST_Controller.php`. + +This is patch for codeigniter-restserver 2.7.2: + +~~~diff +--- a/application/libraries/REST_Controller.php ++++ b/application/libraries/REST_Controller.php +@@ -653,6 +653,11 @@ abstract class REST_Controller extends CI_Controller { + { + call_user_func_array([$this, $controller_method], $arguments); + } ++ catch (CIPHPUnitTestExitException $ex) ++ { ++ // This block is for ci-phpunit-test ++ throw $ex; ++ } + catch (Exception $ex) + { + // If the method doesn't exist, then the error will be caught and an error response shown +~~~ + +Then, you can write test case class like this: + +*tests/controllers/api/Example_test.php* +~~~php +request('GET', 'api/example/users'); + } catch (CIPHPUnitTestExitException $e) { + $output = ob_get_clean(); + } + + $this->assertEquals( + '[{"id":1,"name":"John","email":"john@example.com","fact":"Loves coding"},{"id":2,"name":"Jim","email":"jim@example.com","fact":"Developed on CodeIgniter"},{"id":3,"name":"Jane","email":"jane@example.com","fact":"Lives in the USA","0":{"hobbies":["guitar","cycling"]}}]', + $output + ); + $this->assertResponseCode(200); + } +} +~~~ + +And if you copy sample api controllers, you must change `require` statement to `require_once`: + +~~~diff +--- a/application/controllers/api/Example.php ++++ b/application/controllers/api/Example.php +@@ -3,7 +3,7 @@ + defined('BASEPATH') OR exit('No direct script access allowed'); + + // This can be removed if you use __autoload() in config.php OR use Modular Extensions +-require APPPATH . '/libraries/REST_Controller.php'; ++require_once APPPATH . '/libraries/REST_Controller.php'; + + /** + * This is an example of a few basic user interaction methods you could use +~~~ + +If you require `REST_Controller.php` more than once, you get `Fatal error: Cannot redeclare class REST_Controller`. + +See [working sample](https://github.com/kenjis/ci-app-for-ci-phpunit-test/tree/v0.12.0/application/tests/controllers/api). + +#### [Modular Extensions - HMVC](https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc) + +It seems some users try to work ci-phpunit-test with the HMVC, and they work mostly. But the HMVC is a very complex system, and is against CodeIgniter's basic design. It brings complexity to CodeIgniter. + +There is a known limitation: +See . + +And if you have an issue, please report it to: + +See [working sample](https://github.com/kenjis/ci-hmvc-ci-phpunit-test). diff --git a/vendor/kenjis/ci-phpunit-test/docs/RequestsToCodeIgniter.md b/vendor/kenjis/ci-phpunit-test/docs/RequestsToCodeIgniter.md new file mode 100644 index 00000000000..e75dc2460a6 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/docs/RequestsToCodeIgniter.md @@ -0,0 +1,40 @@ +# Requests to CodeIgniter + +Writing tests for CodeIgniter 3.0 application has troublesome. Because CodeIgniter has some code which is untestable or difficult to test. + +This is request list for CodeIgniter 3.x. + +## Bootstrap file for PHPUnit + +`core/CodeIgniter.php` calls a controller. We need bootstrap file which does not call controllers for testing. + +## Functions which don't call `exit()` + +* `redirect()` +* `show_error()` +* `show_404()` + +`exit()` stops phpunit. We hope they throw exceptions. + +## Output class has status code + +If so, we can test the status code. + +## Output class has cookie data + +If so, we can test the cookie data. + +## Input class has no static variables in methods + +We can't reset the static variable below. It makes difficult to run another test using headers. + +~~~php + public function get_request_header($index, $xss_clean = FALSE) + { + static $headers; + ... +~~~ + +## Way to reset CodeIgniter object + +To test singleton, we need a method to reset it. diff --git a/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/PHP-Parser_LICENSE.md b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/PHP-Parser_LICENSE.md new file mode 100644 index 00000000000..6f3277f0fa7 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/PHP-Parser_LICENSE.md @@ -0,0 +1,35 @@ +PHP-Parser 2.0.0 + +--- + +Copyright (c) 2011 by Nikita Popov. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/Patchwork_LICENSE.md b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/Patchwork_LICENSE.md new file mode 100644 index 00000000000..51104c3c5b9 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/Patchwork_LICENSE.md @@ -0,0 +1,25 @@ + + +--- + +The MIT License + +Copyright (c) 2010-2014 Ignas Rudaitis + +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/kenjis/ci-phpunit-test/docs/third_party_licenses/SoftMocks_LICENSE.md b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/SoftMocks_LICENSE.md new file mode 100644 index 00000000000..01783e2c027 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/SoftMocks_LICENSE.md @@ -0,0 +1,25 @@ + + +--- + +The MIT License (MIT) + +Copyright (c) 2016 Badoo Development + +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/kenjis/ci-phpunit-test/docs/third_party_licenses/monkey_LICENSE.md b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/monkey_LICENSE.md new file mode 100644 index 00000000000..6c58765dfec --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/docs/third_party_licenses/monkey_LICENSE.md @@ -0,0 +1,25 @@ + + +--- + +The MIT License (MIT) + +Copyright (c) 2014 Adrian Philipp + +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/kenjis/ci-phpunit-test/install.php b/vendor/kenjis/ci-phpunit-test/install.php new file mode 100644 index 00000000000..8131737bf72 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/install.php @@ -0,0 +1,18 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +require __DIR__ . '/Installer.php'; + +$app = 'application'; +if (isset($argv[1]) && is_dir($argv[1])) { + $app = $argv[1]; +} +$installer = new Installer(); +$installer->install($app); diff --git a/vendor/kenjis/ci-phpunit-test/update.php b/vendor/kenjis/ci-phpunit-test/update.php new file mode 100644 index 00000000000..1a605442883 --- /dev/null +++ b/vendor/kenjis/ci-phpunit-test/update.php @@ -0,0 +1,18 @@ + + * @license MIT License + * @copyright 2015 Kenji Suzuki + * @link https://github.com/kenjis/ci-phpunit-test + */ + +require __DIR__ . '/Installer.php'; + +$app = 'application'; +if (isset($argv[1]) && is_dir($argv[1])) { + $app = $argv[1]; +} +$installer = new Installer(); +$installer->update($app); diff --git a/vendor/mikey179/vfsStream/.gitignore b/vendor/mikey179/vfsStream/.gitignore new file mode 100644 index 00000000000..2258246f32b --- /dev/null +++ b/vendor/mikey179/vfsStream/.gitignore @@ -0,0 +1,2 @@ +/composer.lock +/nbproject diff --git a/vendor/mikey179/vfsStream/.travis.yml b/vendor/mikey179/vfsStream/.travis.yml new file mode 100644 index 00000000000..8509d3f8a8a --- /dev/null +++ b/vendor/mikey179/vfsStream/.travis.yml @@ -0,0 +1,11 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - wget -nc http://getcomposer.org/composer.phar + - php composer.phar install --dev + +script: phpunit --coverage-text \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/CHANGES b/vendor/mikey179/vfsStream/CHANGES new file mode 100644 index 00000000000..3fb6b8ff543 --- /dev/null +++ b/vendor/mikey179/vfsStream/CHANGES @@ -0,0 +1,158 @@ +1.1.0, 2012-08-25 +================== +- implemented issue #11: add support for streamWrapper::stream_metadata() + vfsStream now supports touch(), chown(), chgrp() and chmod() +- implemented issue #33: add support for stream_truncate + (provided by https://github.com/nikcorg) +- implemented issue #35: size limit (quota) for VFS + + +1.0.0, 2012-05-15 +================== +- raised requirement for PHP version to 5.3.0 +- migrated codebase to use namespaces +- changed distribution from PEAR to Composer +- implemented issue #30: support "c" mode for fopen() +- fixed issue #31: prohibit aquiring locks when already locked / release lock on + fclose() +- fixed issue #32: problems when subfolder has same name as folder +- fixed issue #36: vfsStreamWrapper::stream_open should return false while + trying to open existing non-writable file, patch provided by Alexander Peresypkin + + +0.11.2, 2012-01-14 +================== +- fixed issue #29: set permissions properly when using + vfsStream::copyFromFileSystem(), patch provided by predakanga +- fixed failing tests under PHP > 5.3.2 + + +0.11.1, 2011-12-04 +================== +- fixed issue #28: mkdir overwrites existing directories/files + + +0.11.0, 2011-11-29 +================== +- implemented issue #20: vfsStream::create() removes old structure +- implemented issue #4: possibility to copy structure from existing file system +- fixed issue #23: unlink should not remove any directory +- fixed issue #25: vfsStreamDirectory::hasChild() gives false positives for + nested paths, patch provided by Andrew Coulton +- fixed issue #26: opening a file for reading only should not update its + modification time, reported and initial patch provided by Ludovic Chabant + + +0.10.1, 2011-08-22 +================== +- fixed issue #16: replace vfsStreamContent to vfsStreamContainer for + autocompletion +- fixed issue #17: vfsStream::create() has issues with numeric directories, + patch provided by mathieuk + + +0.10.0, 2011-07-22 +================== +- added new method vfsStreamContainer::hasChildren() and + vfsStreamDirectory::hasChildren() +- implemented issue #14: less verbose way to initialize vfsStream +- implemented issue #13: remove deprecated method vfsStreamContent::setFilemtime() +- implemented issue #6: locking meachanism for files +- ensured that stream_set_blocking(), stream_set_timeout() and + stream_set_write_buffer() on vfsStream urls have the same behaviour + with PHP 5.2 and 5.3 +- implemented issue #10: method to print directory structure + + +0.9.0, 2011-07-13 +================= +- implemented feature request issue #7: add support for fileatime() and filectime() +- fixed issue #3: add support for streamWrapper::stream_cast() +- fixed issue #9: resolve path not called everywhere its needed +- deprecated vfsStreamAbstractContent::setFilemtime(), use + vfsStreamAbstractContent::lastModified() instead, will be removed with 0.10.0 + + +0.8.0, 2010-10-08 +================= +- implemented enhancement #6: use vfsStream::umask() to influence initial file + mode for files and directories +- implemented enhancement #19: support of .. in the url, patch provided by + Guislain Duthieuw +- fixed issue #18: getChild() returns NULL when child's name contains parent name +- fixed bug with incomplete error message when accessing non-existing files on + root level + + +0.7.0, 2010-06-08 +================= +- added new vfsStream::setup() method to simplify vfsStream usage +- fixed issue #15: mkdir creates a subfolder in a folder without permissions + + +0.6.0, 2010-02-15 +================= +- added support for $mode param when opening files, implements enhancement #7 + and fixes issue #13 +- vfsStreamWrapper::stream_open() now evaluates $options for STREAM_REPORT_ERRORS + + +0.5.0, 2010-01-25 +================= +- added support for rename(), patch provided by Benoit Aubuchon +- added support for . as directory alias so that vfs://foo/. resolves to + vfs://foo, can be used as workaround for bug #8 + + +0.4.0, 2009-07-13 +================= +- added support for file modes, users and groups (with restrictions, see + http://code.google.com/p/bovigo/wiki/vfsStreamDocsKnownIssues) +- fixed bug #5: vfsStreamDirectory::addChild() does not replace child with same + name +- fixed bug with is_writable() because of missing stat() fields, patch provided + by Sergey Galkin + + +0.3.2, 2009-02-16 +================= +- support trailing slashes on directories in vfsStream urls, patch provided by + Gabriel Birke +- fixed bug #4: vfsstream can only be read once, reported by Christoph Bloemer +- enabled multiple iterations at the same time over the same directory + + +0.3.1, 2008-02-18 +================= +- fixed path/directory separator issues under linux systems +- fixed uid/gid issues under linux systems + + +0.3.0, 2008-01-02 +================= +- added support for rmdir() +- added vfsStream::newDirectory(), dropped vfsStreamDirectory::ceate() +- added new interface vfsStreamContainer +- added vfsStreamContent::at() which allows code like + $file = vfsStream::newFile('file.txt.')->withContent('foo')->at($otherDir); +- added vfsStreamContent::lastModified(), made vfsStreamContent::setFilemtime() + an alias for this +- moved from Stubbles development environment to bovigo +- refactorings to reduce crap index of various methods + + +0.2.0, 2007-12-29 +================= +- moved vfsStreamWrapper::PROTOCOL to vfsStream::SCHEME +- added new vfsStream::url() method to assist in creating correct vfsStream urls +- added vfsStream::path() method as opposite to vfsStream::url() +- a call to vfsStreamWrapper::register() will now reset the root to null, + implemented on request from David Zuelke +- added support for is_readable(), is_dir(), is_file() +- added vfsStream::newFile() to be able to do + $file = vfsStream::newFile("foo.txt")->withContent("bar"); + + +0.1.0, 2007-12-14 +================= +Initial release. diff --git a/vendor/mikey179/vfsStream/LICENSE b/vendor/mikey179/vfsStream/LICENSE new file mode 100644 index 00000000000..7cca32a1301 --- /dev/null +++ b/vendor/mikey179/vfsStream/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2007-2012, Frank Kleine +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of Stubbles nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/composer.json b/vendor/mikey179/vfsStream/composer.json new file mode 100644 index 00000000000..f21f45219ae --- /dev/null +++ b/vendor/mikey179/vfsStream/composer.json @@ -0,0 +1,12 @@ +{ + "name": "mikey179/vfsStream", + "type": "library", + "homepage": "http://vfs.bovigo.org/", + "license": "BSD", + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "org\\bovigo\\vfs": "src/main/php" } + } +} \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/Example.php b/vendor/mikey179/vfsStream/examples/Example.php new file mode 100644 index 00000000000..a12caeec387 --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/Example.php @@ -0,0 +1,54 @@ +id = $id; + } + + /** + * sets the directory + * + * @param string $directory + */ + public function setDirectory($directory) + { + $this->directory = $directory . DIRECTORY_SEPARATOR . $this->id; + if (file_exists($this->directory) === false) { + mkdir($this->directory, 0700, true); + } + } + + // more source code here... +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/ExampleTestCaseOldWay.php b/vendor/mikey179/vfsStream/examples/ExampleTestCaseOldWay.php new file mode 100644 index 00000000000..4ecb9d8d1a9 --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/ExampleTestCaseOldWay.php @@ -0,0 +1,48 @@ +assertFalse(file_exists(__DIR__ . '/id')); + $example->setDirectory(__DIR__); + $this->assertTrue(file_exists(__DIR__ . '/id')); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/ExampleTestCaseWithVfsStream.php b/vendor/mikey179/vfsStream/examples/ExampleTestCaseWithVfsStream.php new file mode 100644 index 00000000000..97c8a374f6e --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/ExampleTestCaseWithVfsStream.php @@ -0,0 +1,47 @@ +root = vfsStream::setup('exampleDir'); + } + + /** + * @test + */ + public function directoryIsCreated() + { + $example = new Example('id'); + $this->assertFalse($this->root->hasChild('id')); + $example->setDirectory(vfsStream::url('exampleDir')); + $this->assertTrue($this->root->hasChild('id')); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/FailureExample.php b/vendor/mikey179/vfsStream/examples/FailureExample.php new file mode 100644 index 00000000000..472468b2eb5 --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/FailureExample.php @@ -0,0 +1,50 @@ +filename = $filename; + } + + /** + * sets the directory + * + * @param string $directory + */ + public function writeData($data) + { + $bytes = @file_put_contents($this->filename, $data); + if (false === $bytes) { + return 'could not write data'; + } + + return 'ok'; + } + + // more source code here... +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/FailureExampleTestCase.php b/vendor/mikey179/vfsStream/examples/FailureExampleTestCase.php new file mode 100644 index 00000000000..e212305582f --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/FailureExampleTestCase.php @@ -0,0 +1,58 @@ +root = vfsStream::setup('exampleDir'); + } + + /** + * @test + */ + public function returnsOkOnNoFailure() + { + $example = new FailureExample(vfsStream::url('exampleDir/test.txt')); + $this->assertSame('ok', $example->writeData('testdata')); + $this->assertTrue($this->root->hasChild('test.txt')); + $this->assertSame('testdata', $this->root->getChild('test.txt')->getContent()); + } + + /** + * @test + */ + public function returnsErrorMessageIfWritingToFileFails() + { + $file = vfsStream::newFile('test.txt', 0000) + ->withContent('notoverwritten') + ->at($this->root); + $example = new FailureExample(vfsStream::url('exampleDir/test.txt')); + $this->assertSame('could not write data', $example->writeData('testdata')); + $this->assertTrue($this->root->hasChild('test.txt')); + $this->assertSame('notoverwritten', $this->root->getChild('test.txt')->getContent()); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/FileModeExampleTestCaseOldWay.php b/vendor/mikey179/vfsStream/examples/FileModeExampleTestCaseOldWay.php new file mode 100644 index 00000000000..9f99671101f --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/FileModeExampleTestCaseOldWay.php @@ -0,0 +1,67 @@ +setDirectory(__DIR__); + if (DIRECTORY_SEPARATOR === '\\') { + // can not really test on windows, filemode from mkdir() is ignored + $this->assertEquals(40777, decoct(fileperms(__DIR__ . '/id'))); + } else { + $this->assertEquals(40700, decoct(fileperms(__DIR__ . '/id'))); + } + } + + /** + * test correct file mode for created directory + */ + public function testDirectoryHasCorrectDifferentFilePermissions() + { + $example = new FilemodeExample('id', 0755); + $example->setDirectory(__DIR__); + if (DIRECTORY_SEPARATOR === '\\') { + // can not really test on windows, filemode from mkdir() is ignored + $this->assertEquals(40777, decoct(fileperms(__DIR__ . '/id'))); + } else { + $this->assertEquals(40755, decoct(fileperms(__DIR__ . '/id'))); + } + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/FilePermissionsExample.php b/vendor/mikey179/vfsStream/examples/FilePermissionsExample.php new file mode 100644 index 00000000000..6258a5d74d5 --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/FilePermissionsExample.php @@ -0,0 +1,29 @@ + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/FilePermissionsExampleTestCase.php b/vendor/mikey179/vfsStream/examples/FilePermissionsExampleTestCase.php new file mode 100644 index 00000000000..66466362655 --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/FilePermissionsExampleTestCase.php @@ -0,0 +1,44 @@ +writeConfig(array('foo' => 'bar'), + vfsStream::url('exampleDir/writable.ini') + ); + + // assertions here + } + + /** + * @test + */ + public function directoryNotWritable() + { + vfsStream::setup('exampleDir', 0444); + $example = new FilePermissionsExample(); + $example->writeConfig(array('foo' => 'bar'), + vfsStream::url('exampleDir/notWritable.ini') + ); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/FilemodeExample.php b/vendor/mikey179/vfsStream/examples/FilemodeExample.php new file mode 100644 index 00000000000..c2ac364c111 --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/FilemodeExample.php @@ -0,0 +1,62 @@ +id = $id; + $this->fileMode = $fileMode; + } + + /** + * sets the directory + * + * @param string $directory + */ + public function setDirectory($directory) + { + $this->directory = $directory . DIRECTORY_SEPARATOR . $this->id; + if (file_exists($this->directory) === false) { + mkdir($this->directory, $this->fileMode, true); + } + } + + // more source code here... +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/FilemodeExampleTestCaseWithVfsStream.php b/vendor/mikey179/vfsStream/examples/FilemodeExampleTestCaseWithVfsStream.php new file mode 100644 index 00000000000..675a2c7261a --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/FilemodeExampleTestCaseWithVfsStream.php @@ -0,0 +1,53 @@ +root = vfsStream::setup('exampleDir'); + } + + /** + * test that the directory is created + */ + public function testDirectoryIsCreatedWithDefaultPermissions() + { + $example = new FilemodeExample('id'); + $example->setDirectory(vfsStream::url('exampleDir')); + $this->assertEquals(0700, $this->root->getChild('id')->getPermissions()); + } + + /** + * test that the directory is created + */ + public function testDirectoryIsCreatedWithGivenPermissions() + { + $example = new FilemodeExample('id', 0755); + $example->setDirectory(vfsStream::url('exampleDir')); + $this->assertEquals(0755, $this->root->getChild('id')->getPermissions()); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/examples/bootstrap.php b/vendor/mikey179/vfsStream/examples/bootstrap.php new file mode 100644 index 00000000000..998c43db0f5 --- /dev/null +++ b/vendor/mikey179/vfsStream/examples/bootstrap.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/phpdoc.dist.xml b/vendor/mikey179/vfsStream/phpdoc.dist.xml new file mode 100644 index 00000000000..9cc279721d7 --- /dev/null +++ b/vendor/mikey179/vfsStream/phpdoc.dist.xml @@ -0,0 +1,14 @@ + + + vfsStream API Doc + + docs/api + org\bovigo\vfs + + + docs/api + + + src/main/php + + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/phpunit.xml.dist b/vendor/mikey179/vfsStream/phpunit.xml.dist new file mode 100644 index 00000000000..8d2140edce6 --- /dev/null +++ b/vendor/mikey179/vfsStream/phpunit.xml.dist @@ -0,0 +1,42 @@ + + + + + ./src/test/php + + + + + + src/main/php + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/readme.md b/vendor/mikey179/vfsStream/readme.md new file mode 100644 index 00000000000..1635eddd37d --- /dev/null +++ b/vendor/mikey179/vfsStream/readme.md @@ -0,0 +1,3 @@ +For more information have a look in the [wiki](https://github.com/mikey179/vfsStream/wiki). + +[![Build Status](https://secure.travis-ci.org/mikey179/vfsStream.png)](http://travis-ci.org/mikey179/vfsStream) \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/Quota.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/Quota.php new file mode 100644 index 00000000000..b86ad8cdaa2 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/Quota.php @@ -0,0 +1,87 @@ +amount = $amount; + } + + /** + * create with unlimited space + * + * @return Quota + */ + public static function unlimited() + { + return new self(self::UNLIMITED); + } + + /** + * checks if a quota is set + * + * @return bool + */ + public function isLimited() + { + return self::UNLIMITED < $this->amount; + } + + /** + * checks if given used space exceeda quota limit + * + * + * @param int $usedSpace + * @return int + */ + public function spaceLeft($usedSpace) + { + if (self::UNLIMITED === $this->amount) { + return $usedSpace; + } + + if ($usedSpace >= $this->amount) { + return 0; + } + + $spaceLeft = $this->amount - $usedSpace; + if (0 >= $spaceLeft) { + return 0; + } + + return $spaceLeft; + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStream.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStream.php new file mode 100644 index 00000000000..f68fefd8ac1 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStream.php @@ -0,0 +1,389 @@ + + * array('Core' = array('AbstractFactory' => array('test.php' => 'some text content', + * 'other.php' => 'Some more text content', + * 'Invalid.csv' => 'Something else', + * ), + * 'AnEmptyFolder' => array(), + * 'badlocation.php' => 'some bad content', + * ) + * ) + *
+ * the resulting directory tree will look like this: + *
+     * root
+     * \- Core
+     *  |- badlocation.php
+     *  |- AbstractFactory
+     *  | |- test.php
+     *  | |- other.php
+     *  | \- Invalid.csv
+     *  \- AnEmptyFolder
+     * 
+ * Arrays will become directories with their key as directory name, and + * strings becomes files with their key as file name and their value as file + * content. + * + * @param string $rootDirName name of root directory + * @param int $permissions file permissions of root directory + * @param array $structure directory structure to add under root directory + * @return \org\bovigo\vfs\vfsStreamDirectory + * @since 0.7.0 + * @see https://github.com/mikey179/vfsStream/issues/14 + * @see https://github.com/mikey179/vfsStream/issues/20 + */ + public static function setup($rootDirName = 'root', $permissions = null, array $structure = array()) + { + vfsStreamWrapper::register(); + return self::create($structure, vfsStreamWrapper::setRoot(self::newDirectory($rootDirName, $permissions))); + } + + /** + * creates vfsStream directory structure from an array and adds it to given base dir + * + * Assumed $structure contains an array like this: + * + * array('Core' = array('AbstractFactory' => array('test.php' => 'some text content', + * 'other.php' => 'Some more text content', + * 'Invalid.csv' => 'Something else', + * ), + * 'AnEmptyFolder' => array(), + * 'badlocation.php' => 'some bad content', + * ) + * ) + * + * the resulting directory tree will look like this: + *
+     * baseDir
+     * \- Core
+     *  |- badlocation.php
+     *  |- AbstractFactory
+     *  | |- test.php
+     *  | |- other.php
+     *  | \- Invalid.csv
+     *  \- AnEmptyFolder
+     * 
+ * Arrays will become directories with their key as directory name, and + * strings becomes files with their key as file name and their value as file + * content. + * + * If no baseDir is given it will try to add the structure to the existing + * root directory without replacing existing childs except those with equal + * names. + * + * @param array $structure directory structure to add under root directory + * @param vfsStreamDirectory $baseDir base directory to add structure to + * @return vfsStreamDirectory + * @throws \InvalidArgumentException + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/14 + * @see https://github.com/mikey179/vfsStream/issues/20 + */ + public static function create(array $structure, vfsStreamDirectory $baseDir = null) + { + if (null === $baseDir) { + $baseDir = vfsStreamWrapper::getRoot(); + } + + if (null === $baseDir) { + throw new \InvalidArgumentException('No baseDir given and no root directory set.'); + } + + return self::addStructure($structure, $baseDir); + } + + /** + * helper method to create subdirectories recursively + * + * @param array $structure subdirectory structure to add + * @param vfsStreamDirectory $baseDir directory to add the structure to + * @return vfsStreamDirectory + */ + protected static function addStructure(array $structure, vfsStreamDirectory $baseDir) + { + foreach ($structure as $name => $data) { + $name = (string) $name; + if (is_array($data) === true) { + self::addStructure($data, self::newDirectory($name)->at($baseDir)); + } elseif (is_string($data) === true) { + self::newFile($name)->withContent($data)->at($baseDir); + } + } + + return $baseDir; + } + + /** + * copies the file system structure from given path into the base dir + * + * If no baseDir is given it will try to add the structure to the existing + * root directory without replacing existing childs except those with equal + * names. + * File permissions are copied as well. + * Please note that file contents will only be copied if their file size + * does not exceed the given $maxFileSize which is 1024 KB. + * + * @param string $path path to copy the structure from + * @param vfsStreamDirectory $baseDir directory to add the structure to + * @param int $maxFileSize maximum file size of files to copy content from + * @return vfsStreamDirectory + * @throws \InvalidArgumentException + * @since 0.11.0 + * @see https://github.com/mikey179/vfsStream/issues/4 + */ + public static function copyFromFileSystem($path, vfsStreamDirectory $baseDir = null, $maxFileSize = 1048576) + { + if (null === $baseDir) { + $baseDir = vfsStreamWrapper::getRoot(); + } + + if (null === $baseDir) { + throw new \InvalidArgumentException('No baseDir given and no root directory set.'); + } + + $dir = new \DirectoryIterator($path); + foreach ($dir as $fileinfo) { + if ($fileinfo->isFile() === true) { + if ($fileinfo->getSize() <= $maxFileSize) { + $content = file_get_contents($fileinfo->getPathname()); + } else { + $content = ''; + } + + self::newFile($fileinfo->getFilename(), + octdec(substr(sprintf('%o', $fileinfo->getPerms()), -4)) + ) + ->withContent($content) + ->at($baseDir); + } elseif ($fileinfo->isDir() === true && $fileinfo->isDot() === false) { + self::copyFromFileSystem($fileinfo->getPathname(), + self::newDirectory($fileinfo->getFilename(), + octdec(substr(sprintf('%o', $fileinfo->getPerms()), -4)) + ) + ->at($baseDir), + $maxFileSize + ); + } + } + + return $baseDir; + } + + /** + * returns a new file with given name + * + * @param string $name name of file to create + * @param int $permissions permissions of file to create + * @return vfsStreamFile + */ + public static function newFile($name, $permissions = null) + { + return new vfsStreamFile($name, $permissions); + } + + /** + * returns a new directory with given name + * + * If the name contains slashes, a new directory structure will be created. + * The returned directory will always be the parent directory of this + * directory structure. + * + * @param string $name name of directory to create + * @param int $permissions permissions of directory to create + * @return vfsStreamDirectory + */ + public static function newDirectory($name, $permissions = null) + { + if ('/' === $name{0}) { + $name = substr($name, 1); + } + + $firstSlash = strpos($name, '/'); + if (false === $firstSlash) { + return new vfsStreamDirectory($name, $permissions); + } + + $ownName = substr($name, 0, $firstSlash); + $subDirs = substr($name, $firstSlash + 1); + $directory = new vfsStreamDirectory($ownName, $permissions); + self::newDirectory($subDirs, $permissions)->at($directory); + return $directory; + } + + /** + * returns current user + * + * If the system does not support posix_getuid() the current user will be root (0). + * + * @return int + */ + public static function getCurrentUser() + { + return function_exists('posix_getuid') ? posix_getuid() : self::OWNER_ROOT; + } + + /** + * returns current group + * + * If the system does not support posix_getgid() the current group will be root (0). + * + * @return int + */ + public static function getCurrentGroup() + { + return function_exists('posix_getgid') ? posix_getgid() : self::GROUP_ROOT; + } + + /** + * use visitor to inspect a content structure + * + * If the given content is null it will fall back to use the current root + * directory of the stream wrapper. + * + * Returns given visitor for method chaining comfort. + * + * @param vfsStreamVisitor $visitor the visitor who inspects + * @param vfsStreamContent $content directory structure to inspect + * @return vfsStreamVisitor + * @throws \InvalidArgumentException + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/10 + */ + public static function inspect(vfsStreamVisitor $visitor, vfsStreamContent $content = null) + { + if (null !== $content) { + return $visitor->visit($content); + } + + $root = vfsStreamWrapper::getRoot(); + if (null === $root) { + throw new \InvalidArgumentException('No content given and no root directory set.'); + } + + return $visitor->visitDirectory($root); + } + + /** + * sets quota to given amount of bytes + * + * @param int $bytes + * @since 1.1.0 + */ + public static function setQuota($bytes) + { + vfsStreamWrapper::setQuota(new Quota($bytes)); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamAbstractContent.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamAbstractContent.php new file mode 100644 index 00000000000..d3d2d614444 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamAbstractContent.php @@ -0,0 +1,375 @@ +name = $name; + $time = time(); + if (null === $permissions) { + $permissions = $this->getDefaultPermissions() & ~vfsStream::umask(); + } + + $this->lastAccessed = $time; + $this->lastAttributeModified = $time; + $this->lastModified = $time; + $this->permissions = $permissions; + $this->user = vfsStream::getCurrentUser(); + $this->group = vfsStream::getCurrentGroup(); + } + + /** + * returns default permissions for concrete implementation + * + * @return int + * @since 0.8.0 + */ + protected abstract function getDefaultPermissions(); + + /** + * returns the file name of the content + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * renames the content + * + * @param string $newName + */ + public function rename($newName) + { + $this->name = $newName; + } + + /** + * checks whether the container can be applied to given name + * + * @param string $name + * @return bool + */ + public function appliesTo($name) + { + if ($name === $this->name) { + return true; + } + + $segment_name = $this->name.'/'; + return (strncmp($segment_name, $name, strlen($segment_name)) == 0); + } + + /** + * returns the type of the container + * + * @return int + */ + public function getType() + { + return $this->type; + } + + /** + * sets the last modification time of the stream content + * + * @param int $filemtime + * @return vfsStreamContent + */ + public function lastModified($filemtime) + { + $this->lastModified = $filemtime; + return $this; + } + + /** + * returns the last modification time of the stream content + * + * @return int + */ + public function filemtime() + { + return $this->lastModified; + } + + /** + * sets last access time of the stream content + * + * @param int $fileatime + * @return vfsStreamContent + * @since 0.9 + */ + public function lastAccessed($fileatime) + { + $this->lastAccessed = $fileatime; + return $this; + } + + /** + * returns the last access time of the stream content + * + * @return int + * @since 0.9 + */ + public function fileatime() + { + return $this->lastAccessed; + } + + /** + * sets the last attribute modification time of the stream content + * + * @param int $filectime + * @return vfsStreamContent + * @since 0.9 + */ + public function lastAttributeModified($filectime) + { + $this->lastAttributeModified = $filectime; + return $this; + } + + /** + * returns the last attribute modification time of the stream content + * + * @return int + * @since 0.9 + */ + public function filectime() + { + return $this->lastAttributeModified; + } + + /** + * adds content to given container + * + * @param vfsStreamContainer $container + * @return vfsStreamContent + */ + public function at(vfsStreamContainer $container) + { + $container->addChild($this); + return $this; + } + + /** + * change file mode to given permissions + * + * @param int $permissions + * @return vfsStreamContent + */ + public function chmod($permissions) + { + $this->permissions = $permissions; + $this->lastAttributeModified = time(); + clearstatcache(); + return $this; + } + + /** + * returns permissions + * + * @return int + */ + public function getPermissions() + { + return $this->permissions; + } + + /** + * checks whether content is readable + * + * @param int $user id of user to check for + * @param int $group id of group to check for + * @return bool + */ + public function isReadable($user, $group) + { + if ($this->user === $user) { + $check = 0400; + } elseif ($this->group === $group) { + $check = 0040; + } else { + $check = 0004; + } + + return (bool) ($this->permissions & $check); + } + + /** + * checks whether content is writable + * + * @param int $user id of user to check for + * @param int $group id of group to check for + * @return bool + */ + public function isWritable($user, $group) + { + if ($this->user === $user) { + $check = 0200; + } elseif ($this->group === $group) { + $check = 0020; + } else { + $check = 0002; + } + + return (bool) ($this->permissions & $check); + } + + /** + * checks whether content is executable + * + * @param int $user id of user to check for + * @param int $group id of group to check for + * @return bool + */ + public function isExecutable($user, $group) + { + if ($this->user === $user) { + $check = 0100; + } elseif ($this->group === $group) { + $check = 0010; + } else { + $check = 0001; + } + + return (bool) ($this->permissions & $check); + } + + /** + * change owner of file to given user + * + * @param int $user + * @return vfsStreamContent + */ + public function chown($user) + { + $this->user = $user; + $this->lastAttributeModified = time(); + return $this; + } + + /** + * checks whether file is owned by given user + * + * @param int $user + * @return bool + */ + public function isOwnedByUser($user) + { + return $this->user === $user; + } + + /** + * returns owner of file + * + * @return int + */ + public function getUser() + { + return $this->user; + } + + /** + * change owner group of file to given group + * + * @param int $group + * @return vfsStreamContent + */ + public function chgrp($group) + { + $this->group = $group; + $this->lastAttributeModified = time(); + return $this; + } + + /** + * checks whether file is owned by group + * + * @param int $group + * @return bool + */ + public function isOwnedByGroup($group) + { + return $this->group === $group; + } + + /** + * returns owner group of file + * + * @return int + */ + public function getGroup() + { + return $this->group; + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContainer.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContainer.php new file mode 100644 index 00000000000..e27a5810700 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContainer.php @@ -0,0 +1,62 @@ + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContainerIterator.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContainerIterator.php new file mode 100644 index 00000000000..0f3e3a7912a --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContainerIterator.php @@ -0,0 +1,90 @@ +children = $children; + reset($this->children); + } + + /** + * resets children pointer + */ + public function rewind() + { + reset($this->children); + } + + /** + * returns the current child + * + * @return vfsStreamContent + */ + public function current() + { + $child = current($this->children); + if (false === $child) { + return null; + } + + return $child; + } + + /** + * returns the name of the current child + * + * @return string + */ + public function key() + { + $child = current($this->children); + if (false === $child) { + return null; + } + + return $child->getName(); + } + + /** + * iterates to next child + */ + public function next() + { + next($this->children); + } + + /** + * checks if the current value is valid + * + * @return bool + */ + public function valid() + { + return (false !== current($this->children)); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContent.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContent.php new file mode 100644 index 00000000000..197c51aec7c --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamContent.php @@ -0,0 +1,182 @@ + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamDirectory.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamDirectory.php new file mode 100644 index 00000000000..229dcd04f56 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamDirectory.php @@ -0,0 +1,235 @@ +type = vfsStreamContent::TYPE_DIR; + parent::__construct($name, $permissions); + } + + /** + * returns default permissions for concrete implementation + * + * @return int + * @since 0.8.0 + */ + protected function getDefaultPermissions() + { + return 0777; + } + + /** + * returns size of directory + * + * The size of a directory is always 0 bytes. To calculate the summarized + * size of all children in the directory use sizeSummarized(). + * + * @return int + */ + public function size() + { + return 0; + } + + /** + * returns summarized size of directory and its children + * + * @return int + */ + public function sizeSummarized() + { + $size = 0; + foreach ($this->children as $child) { + if ($child->getType() === vfsStreamContent::TYPE_DIR) { + $size += $child->sizeSummarized(); + } else { + $size += $child->size(); + } + } + + return $size; + } + + /** + * renames the content + * + * @param string $newName + * @throws vfsStreamException + */ + public function rename($newName) + { + if (strstr($newName, '/') !== false) { + throw new vfsStreamException('Directory name can not contain /.'); + } + + parent::rename($newName); + } + + /** + * adds child to the directory + * + * @param vfsStreamContent $child + */ + public function addChild(vfsStreamContent $child) + { + $this->children[$child->getName()] = $child; + $this->updateModifications(); + } + + /** + * removes child from the directory + * + * @param string $name + * @return bool + */ + public function removeChild($name) + { + foreach ($this->children as $key => $child) { + if ($child->appliesTo($name) === true) { + unset($this->children[$key]); + $this->updateModifications(); + return true; + } + } + + return false; + } + + /** + * updates internal timestamps + */ + protected function updateModifications() + { + $time = time(); + $this->lastAttributeModified = $time; + $this->lastModified = $time; + } + + /** + * checks whether the container contains a child with the given name + * + * @param string $name + * @return bool + */ + public function hasChild($name) + { + return ($this->getChild($name) !== null); + } + + /** + * returns the child with the given name + * + * @param string $name + * @return vfsStreamContent + */ + public function getChild($name) + { + $childName = $this->getRealChildName($name); + foreach ($this->children as $child) { + if ($child->getName() === $childName) { + return $child; + } + + if ($child->appliesTo($childName) === true && $child->hasChild($childName) === true) { + return $child->getChild($childName); + } + } + + return null; + } + + /** + * helper method to detect the real child name + * + * @param string $name + * @return string + */ + protected function getRealChildName($name) + { + if ($this->appliesTo($name) === true) { + return self::getChildName($name, $this->name); + } + + return $name; + } + + /** + * helper method to calculate the child name + * + * @param string $name + * @param string $ownName + * @return string + */ + protected static function getChildName($name, $ownName) + { + if ($name === $ownName) { + return $name; + } + + return substr($name, strlen($ownName) + 1); + } + + /** + * checks whether directory contains any children + * + * @return bool + * @since 0.10.0 + */ + public function hasChildren() + { + return (count($this->children) > 0); + } + + /** + * returns a list of children for this directory + * + * @return vfsStreamContent[] + */ + public function getChildren() + { + return array_values($this->children); + } + + /** + * returns iterator for the children + * + * @return vfsStreamContainerIterator + */ + public function getIterator() + { + return new vfsStreamContainerIterator($this->children); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamException.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamException.php new file mode 100644 index 00000000000..aa79d645d74 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamException.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamFile.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamFile.php new file mode 100644 index 00000000000..2ab3fbe9e8e --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamFile.php @@ -0,0 +1,327 @@ +type = vfsStreamContent::TYPE_FILE; + parent::__construct($name, $permissions); + } + + /** + * returns default permissions for concrete implementation + * + * @return int + * @since 0.8.0 + */ + protected function getDefaultPermissions() + { + return 0666; + } + + /** + * checks whether the container can be applied to given name + * + * @param string $name + * @return bool + */ + public function appliesTo($name) + { + return ($name === $this->name); + } + + /** + * alias for withContent() + * + * @param string $content + * @return vfsStreamFile + * @see withContent() + */ + public function setContent($content) + { + return $this->withContent($content); + } + + /** + * sets the contents of the file + * + * Setting content with this method does not change the time when the file + * was last modified. + * + * @param string $content + * @return vfsStreamFile + */ + public function withContent($content) + { + $this->content = $content; + return $this; + } + + /** + * returns the contents of the file + * + * Getting content does not change the time when the file + * was last accessed. + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * simply open the file + * + * @since 0.9 + */ + public function open() + { + $this->seek(0, SEEK_SET); + $this->lastAccessed = time(); + } + + /** + * open file and set pointer to end of file + * + * @since 0.9 + */ + public function openForAppend() + { + $this->seek(0, SEEK_END); + $this->lastAccessed = time(); + } + + /** + * open file and truncate content + * + * @since 0.9 + */ + public function openWithTruncate() + { + $this->open(); + $this->content = ''; + $time = time(); + $this->lastAccessed = $time; + $this->lastModified = $time; + } + + /** + * reads the given amount of bytes from content + * + * Using this method changes the time when the file was last accessed. + * + * @param int $count + * @return string + */ + public function read($count) + { + $data = substr($this->content, $this->bytes_read, $count); + $this->bytes_read += $count; + $this->lastAccessed = time(); + return $data; + } + + /** + * returns the content until its end from current offset + * + * Using this method changes the time when the file was last accessed. + * + * @return string + */ + public function readUntilEnd() + { + $this->lastAccessed = time(); + return substr($this->content, $this->bytes_read); + } + + /** + * writes an amount of data + * + * Using this method changes the time when the file was last modified. + * + * @param string $data + * @return amount of written bytes + */ + public function write($data) + { + $dataLen = strlen($data); + $this->content = substr($this->content, 0, $this->bytes_read) . $data . substr($this->content, $this->bytes_read + $dataLen); + $this->bytes_read += $dataLen; + $this->lastModified = time(); + return $dataLen; + } + + /** + * Truncates a file to a given length + * + * @param int $size length to truncate file to + * @return bool + * @since 1.1.0 + */ + public function truncate($size) { + if ($size > $this->size()) { + // Pad with null-chars if we're "truncating up" + $this->setContent($this->getContent() . str_repeat("\0", $size - $this->size())); + } else { + $this->setContent(substr($this->getContent(), 0, $size)); + } + + $this->lastModified = time(); + return true; + } + + /** + * checks whether pointer is at end of file + * + * @return bool + */ + public function eof() + { + return $this->bytes_read >= strlen($this->content); + } + + /** + * returns the current position within the file + * + * @return int + */ + public function getBytesRead() + { + return $this->bytes_read; + } + + /** + * seeks to the given offset + * + * @param int $offset + * @param int $whence + * @return bool + */ + public function seek($offset, $whence) + { + switch ($whence) { + case SEEK_CUR: + $this->bytes_read += $offset; + return true; + + case SEEK_END: + $this->bytes_read = strlen($this->content) + $offset; + return true; + + case SEEK_SET: + $this->bytes_read = $offset; + return true; + + default: + return false; + } + + return false; + } + + /** + * returns size of content + * + * @return int + */ + public function size() + { + return strlen($this->content); + } + + + /** + * locks file for + * + * @param int $operation + * @return vfsStreamFile + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/6 + */ + public function lock($operation) + { + if ((LOCK_NB & $operation) == LOCK_NB) { + $this->lock = $operation - LOCK_NB; + } else { + $this->lock = $operation; + } + + return $this; + } + + /** + * checks whether file is locked + * + * @return bool + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/6 + */ + public function isLocked() + { + return (LOCK_UN !== $this->lock); + } + + /** + * checks whether file is locked in shared mode + * + * @return bool + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/6 + */ + public function hasSharedLock() + { + return (LOCK_SH === $this->lock); + } + + /** + * checks whether file is locked in exclusive mode + * + * @return bool + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/6 + */ + public function hasExclusiveLock() + { + return (LOCK_EX === $this->lock); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamWrapper.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamWrapper.php new file mode 100644 index 00000000000..72b76e76da3 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamWrapper.php @@ -0,0 +1,932 @@ +getName() === $path) { + return self::$root; + } + + if (self::$root->hasChild($path) === true) { + return self::$root->getChild($path); + } + + return null; + } + + /** + * returns content for given path but only when it is of given type + * + * @param string $path + * @param int $type + * @return vfsStreamContent + */ + protected function getContentOfType($path, $type) + { + $content = $this->getContent($path); + if (null !== $content && $content->getType() === $type) { + return $content; + } + + return null; + } + + /** + * splits path into its dirname and the basename + * + * @param string $path + * @return string[] + */ + protected function splitPath($path) + { + $lastSlashPos = strrpos($path, '/'); + if (false === $lastSlashPos) { + return array('dirname' => '', 'basename' => $path); + } + + return array('dirname' => substr($path, 0, $lastSlashPos), + 'basename' => substr($path, $lastSlashPos + 1) + ); + } + + /** + * helper method to resolve a path from /foo/bar/. to /foo/bar + * + * @param string $path + * @return string + */ + protected function resolvePath($path) + { + $newPath = array(); + foreach (explode('/', $path) as $pathPart) { + if ('.' !== $pathPart) { + if ('..' !== $pathPart) { + $newPath[] = $pathPart; + } else { + array_pop($newPath); + } + } + } + + return implode('/', $newPath); + } + + /** + * open the stream + * + * @param string $path the path to open + * @param string $mode mode for opening + * @param string $options options for opening + * @param string $opened_path full path that was actually opened + * @return bool + */ + public function stream_open($path, $mode, $options, $opened_path) + { + $extended = ((strstr($mode, '+') !== false) ? (true) : (false)); + $mode = str_replace(array('b', '+'), '', $mode); + if (in_array($mode, array('r', 'w', 'a', 'x', 'c')) === false) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('Illegal mode ' . $mode . ', use r, w, a, x or c, flavoured with b and/or +', E_USER_WARNING); + } + + return false; + } + + $this->mode = $this->calculateMode($mode, $extended); + $path = $this->resolvePath(vfsStream::path($path)); + $this->content = $this->getContentOfType($path, vfsStreamContent::TYPE_FILE); + if (null !== $this->content) { + if (self::WRITE === $mode) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('File ' . $path . ' already exists, can not open with mode x', E_USER_WARNING); + } + + return false; + } + + if ( + (self::TRUNCATE === $mode || self::APPEND === $mode) && + $this->content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false + ) { + return false; + } + + if (self::TRUNCATE === $mode) { + $this->content->openWithTruncate(); + } elseif (self::APPEND === $mode) { + $this->content->openForAppend(); + } else { + if (!$this->content->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('Permission denied', E_USER_WARNING); + } + return false; + } + $this->content->open(); + } + + return true; + } + + $content = $this->createFile($path, $mode, $options); + if (false === $content) { + return false; + } + + $this->content = $content; + return true; + } + + /** + * creates a file at given path + * + * @param string $path the path to open + * @param string $mode mode for opening + * @param string $options options for opening + * @return bool + */ + private function createFile($path, $mode = null, $options = null) + { + $names = $this->splitPath($path); + if (empty($names['dirname']) === true) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('File ' . $names['basename'] . ' does not exist', E_USER_WARNING); + } + + return false; + } + + $dir = $this->getContentOfType($names['dirname'], vfsStreamContent::TYPE_DIR); + if (null === $dir) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('Directory ' . $names['dirname'] . ' does not exist', E_USER_WARNING); + } + + return false; + } elseif ($dir->hasChild($names['basename']) === true) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('Directory ' . $names['dirname'] . ' already contains a director named ' . $names['basename'], E_USER_WARNING); + } + + return false; + } + + if (self::READ === $mode) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('Can not open non-existing file ' . $path . ' for reading', E_USER_WARNING); + } + + return false; + } + + if ($dir->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) { + trigger_error('Can not create new file in non-writable path ' . $names['dirname'], E_USER_WARNING); + } + + return false; + } + + return vfsStream::newFile($names['basename'])->at($dir); + } + + /** + * calculates the file mode + * + * @param string $mode opening mode: r, w, a or x + * @param bool $extended true if + was set with opening mode + * @return int + */ + protected function calculateMode($mode, $extended) + { + if (true === $extended) { + return self::ALL; + } + + if (self::READ === $mode) { + return self::READONLY; + } + + return self::WRITEONLY; + } + + /** + * closes the stream + */ + public function stream_close() + { + $this->content->lock(LOCK_UN); + } + + /** + * read the stream up to $count bytes + * + * @param int $count amount of bytes to read + * @return string + */ + public function stream_read($count) + { + if (self::WRITEONLY === $this->mode) { + return ''; + } + + if ($this->content->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return ''; + } + + return $this->content->read($count); + } + + /** + * writes data into the stream + * + * @param string $data + * @return int amount of bytes written + */ + public function stream_write($data) + { + if (self::READONLY === $this->mode) { + return 0; + } + + if ($this->content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return 0; + } + + if (self::$quota->isLimited()) { + $data = substr($data, 0, self::$quota->spaceLeft(self::$root->sizeSummarized())); + } + + return $this->content->write($data); + } + + /** + * truncates a file to a given length + * + * @param int $size length to truncate file to + * @return bool + * @since 1.1.0 + */ + public function stream_truncate($size) + { + if (self::READONLY === $this->mode) { + return false; + } + + if ($this->content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return false; + } + + if ($this->content->getType() !== vfsStreamContent::TYPE_FILE) { + return false; + } + + if (self::$quota->isLimited() && $this->content->size() < $size) { + $maxSize = self::$quota->spaceLeft(self::$root->sizeSummarized()); + if (0 === $maxSize) { + return false; + } + + if ($size > $maxSize) { + $size = $maxSize; + } + } + + return $this->content->truncate($size); + } + + /** + * sets metadata like owner, user or permissions + * + * @param string $path + * @param int $option + * @param mixed $var + * @return bool + * @since 1.1.0 + */ + public function stream_metadata($path, $option, $var) + { + $path = $this->resolvePath(vfsStream::path($path)); + $content = $this->getContent($path); + switch ($option) { + case STREAM_META_TOUCH: + if (null === $content) { + $content = $this->createFile($path); + } + + if (isset($var[0])) { + $content->lastModified($var[0]); + } + + if (isset($var[1])) { + $content->lastAccessed($var[1]); + } + + return true; + + case STREAM_META_OWNER_NAME: + return false; + + case STREAM_META_OWNER: + if (null === $content) { + return false; + } + + $content->chown($var); + return true; + + case STREAM_META_GROUP_NAME: + return false; + + case STREAM_META_GROUP: + if (null === $content) { + return false; + } + + $content->chgrp($var); + return true; + + case STREAM_META_ACCESS: + if (null === $content) { + return false; + } + + $content->chmod($var); + return true; + + default: + return false; + } + } + + /** + * checks whether stream is at end of file + * + * @return bool + */ + public function stream_eof() + { + return $this->content->eof(); + } + + /** + * returns the current position of the stream + * + * @return int + */ + public function stream_tell() + { + return $this->content->getBytesRead(); + } + + /** + * seeks to the given offset + * + * @param int $offset + * @param int $whence + * @return bool + */ + public function stream_seek($offset, $whence) + { + return $this->content->seek($offset, $whence); + } + + /** + * flushes unstored data into storage + * + * @return bool + */ + public function stream_flush() + { + return true; + } + + /** + * returns status of stream + * + * @return array + */ + public function stream_stat() + { + $fileStat = array('dev' => 0, + 'ino' => 0, + 'mode' => $this->content->getType() | $this->content->getPermissions(), + 'nlink' => 0, + 'uid' => $this->content->getUser(), + 'gid' => $this->content->getGroup(), + 'rdev' => 0, + 'size' => $this->content->size(), + 'atime' => $this->content->fileatime(), + 'mtime' => $this->content->filemtime(), + 'ctime' => $this->content->filectime(), + 'blksize' => -1, + 'blocks' => -1 + ); + return array_merge(array_values($fileStat), $fileStat); + } + + /** + * retrieve the underlaying resource + * + * Please note that this method always returns false as there is no + * underlaying resource to return. + * + * @param int $cast_as + * @since 0.9.0 + * @see https://github.com/mikey179/vfsStream/issues/3 + * @return bool + */ + public function stream_cast($cast_as) + { + return false; + } + + /** + * set lock status for stream + * + * @param int $operation + * @return bool + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/6 + * @see https://github.com/mikey179/vfsStream/issues/31 + */ + public function stream_lock($operation) + { + if ((LOCK_NB & $operation) == LOCK_NB) { + $operation = $operation - LOCK_NB; + } + + if (LOCK_EX === $operation && $this->content->isLocked()) { + return false; + } elseif (LOCK_SH === $operation && $this->content->hasExclusiveLock()) { + return false; + } + + $this->content->lock($operation); + return true; + } + + /** + * sets options on the stream + * + * @param int $option key of option to set + * @param int $arg1 + * @param int $arg2 + * @return bool + * @since 0.10.0 + * @see https://github.com/mikey179/vfsStream/issues/15 + * @see http://www.php.net/manual/streamwrapper.stream-set-option.php + */ + public function stream_set_option($option, $arg1, $arg2) + { + switch ($option) { + case STREAM_OPTION_BLOCKING: + // break omitted + + case STREAM_OPTION_READ_TIMEOUT: + // break omitted + + case STREAM_OPTION_WRITE_BUFFER: + // break omitted + + default: + // nothing to do here + } + + return false; + } + + /** + * remove the data under the given path + * + * @param string $path + * @return bool + */ + public function unlink($path) + { + $realPath = $this->resolvePath(vfsStream::path($path)); + $content = $this->getContent($realPath); + if (null === $content || $content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return false; + } + + if ($content->getType() !== vfsStreamContent::TYPE_FILE) { + trigger_error('unlink(' . $path . '): Operation not permitted', E_USER_WARNING); + return false; + } + + return $this->doUnlink($realPath); + } + + /** + * removes a path + * + * @param string $path + * @return bool + */ + protected function doUnlink($path) + { + if (self::$root->getName() === $path) { + // delete root? very brave. :) + self::$root = null; + clearstatcache(); + return true; + } + + $names = $this->splitPath($path); + $content = $this->getContent($names['dirname']); + if ($content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return false; + } + + clearstatcache(); + return $content->removeChild($names['basename']); + } + + /** + * rename from one path to another + * + * @param string $path_from + * @param string $path_to + * @return bool + * @author Benoit Aubuchon + */ + public function rename($path_from, $path_to) + { + $srcRealPath = $this->resolvePath(vfsStream::path($path_from)); + $dstRealPath = $this->resolvePath(vfsStream::path($path_to)); + $srcContent = $this->getContent($srcRealPath); + if (null == $srcContent) { + trigger_error(' No such file or directory', E_USER_WARNING); + return false; + } + $dstNames = $this->splitPath($dstRealPath); + $dstParentContent = $this->getContent($dstNames['dirname']); + if (null == $dstParentContent) { + trigger_error('No such file or directory', E_USER_WARNING); + return false; + } + if (!$dstParentContent->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) { + trigger_error('Permission denied', E_USER_WARNING); + return false; + } + if ($dstParentContent->getType() !== vfsStreamContent::TYPE_DIR) { + trigger_error('Target is not a directory', E_USER_WARNING); + return false; + } + + $dstContent = clone $srcContent; + // Renaming the filename + $dstContent->rename($dstNames['basename']); + // Copying to the destination + $dstParentContent->addChild($dstContent); + // Removing the source + return $this->doUnlink($srcRealPath); + } + + /** + * creates a new directory + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($path, $mode, $options) + { + $umask = vfsStream::umask(); + if (0 < $umask) { + $permissions = $mode & ~$umask; + } else { + $permissions = $mode; + } + + $path = $this->resolvePath(vfsStream::path($path)); + if (null !== $this->getContent($path)) { + trigger_error('mkdir(): Path vfs://' . $path . ' exists', E_USER_WARNING); + return false; + } + + if (null === self::$root) { + self::$root = vfsStream::newDirectory($path, $permissions); + return true; + } + + $maxDepth = count(explode('/', $path)); + $names = $this->splitPath($path); + $newDirs = $names['basename']; + $dir = null; + $i = 0; + while ($dir === null && $i < $maxDepth) { + $dir = $this->getContent($names['dirname']); + $names = $this->splitPath($names['dirname']); + if (null == $dir) { + $newDirs = $names['basename'] . '/' . $newDirs; + } + + $i++; + } + + if (null === $dir + || $dir->getType() !== vfsStreamContent::TYPE_DIR + || $dir->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return false; + } + + $recursive = ((STREAM_MKDIR_RECURSIVE & $options) !== 0) ? (true) : (false); + if (strpos($newDirs, '/') !== false && false === $recursive) { + return false; + } + + vfsStream::newDirectory($newDirs, $permissions)->at($dir); + return true; + } + + /** + * removes a directory + * + * @param string $path + * @param int $options + * @return bool + * @todo consider $options with STREAM_MKDIR_RECURSIVE + */ + public function rmdir($path, $options) + { + $path = $this->resolvePath(vfsStream::path($path)); + $child = $this->getContentOfType($path, vfsStreamContent::TYPE_DIR); + if (null === $child) { + return false; + } + + // can only remove empty directories + if (count($child->getChildren()) > 0) { + return false; + } + + if (self::$root->getName() === $path) { + // delete root? very brave. :) + self::$root = null; + clearstatcache(); + return true; + } + + $names = $this->splitPath($path); + $dir = $this->getContentOfType($names['dirname'], vfsStreamContent::TYPE_DIR); + if ($dir->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return false; + } + + clearstatcache(); + return $dir->removeChild($child->getName()); + } + + /** + * opens a directory + * + * @param string $path + * @param int $options + * @return bool + */ + public function dir_opendir($path, $options) + { + $path = $this->resolvePath(vfsStream::path($path)); + $this->dir = $this->getContentOfType($path, vfsStreamContent::TYPE_DIR); + if (null === $this->dir || $this->dir->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) { + return false; + } + + $this->dirIterator = $this->dir->getIterator(); + return true; + } + + /** + * reads directory contents + * + * @return string + */ + public function dir_readdir() + { + $dir = $this->dirIterator->current(); + if (null === $dir) { + return false; + } + + $this->dirIterator->next(); + return $dir->getName(); + } + + /** + * reset directory iteration + * + * @return bool + */ + public function dir_rewinddir() + { + return $this->dirIterator->rewind(); + } + + /** + * closes directory + * + * @return bool + */ + public function dir_closedir() + { + $this->dirIterator = null; + return true; + } + + /** + * returns status of url + * + * @param string $path path of url to return status for + * @param int $flags flags set by the stream API + * @return array + */ + public function url_stat($path, $flags) + { + $content = $this->getContent($this->resolvePath(vfsStream::path($path))); + if (null === $content) { + if (($flags & STREAM_URL_STAT_QUIET) != STREAM_URL_STAT_QUIET) { + trigger_error(' No such file or directory: ' . $path, E_USER_WARNING); + } + + return false; + + } + + $fileStat = array('dev' => 0, + 'ino' => 0, + 'mode' => $content->getType() | $content->getPermissions(), + 'nlink' => 0, + 'uid' => $content->getUser(), + 'gid' => $content->getGroup(), + 'rdev' => 0, + 'size' => $content->size(), + 'atime' => $content->fileatime(), + 'mtime' => $content->filemtime(), + 'ctime' => $content->filectime(), + 'blksize' => -1, + 'blocks' => -1 + ); + return array_merge(array_values($fileStat), $fileStat); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamAbstractVisitor.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamAbstractVisitor.php new file mode 100644 index 00000000000..6af70b6aece --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamAbstractVisitor.php @@ -0,0 +1,45 @@ +getType()) { + case vfsStreamContent::TYPE_FILE: + $this->visitFile($content); + break; + + case vfsStreamContent::TYPE_DIR: + $this->visitDirectory($content); + break; + + default: + throw new \InvalidArgumentException('Unknown content type ' . $content->getType() . ' for ' . $content->getName()); + } + + return $this; + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamPrintVisitor.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamPrintVisitor.php new file mode 100644 index 00000000000..eb742d342fa --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamPrintVisitor.php @@ -0,0 +1,93 @@ +out = $out; + } + + /** + * visit a file and process it + * + * @param vfsStreamFile $file + * @return vfsStreamPrintVisitor + */ + public function visitFile(vfsStreamFile $file) + { + $this->printContent($file); + return $this; + } + + /** + * visit a directory and process it + * + * @param vfsStreamDirectory $dir + * @return vfsStreamPrintVisitor + */ + public function visitDirectory(vfsStreamDirectory $dir) + { + $this->printContent($dir); + $this->depth++; + foreach ($dir as $child) { + $this->visit($child); + } + + $this->depth--; + return $this; + } + + /** + * helper method to print the content + * + * @param vfsStreamContent $content + */ + protected function printContent(vfsStreamContent $content) + { + fwrite($this->out, str_repeat(' ', $this->depth) . '- ' . $content->getName() . "\n"); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamStructureVisitor.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamStructureVisitor.php new file mode 100644 index 00000000000..bbf38a4f86b --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamStructureVisitor.php @@ -0,0 +1,98 @@ +reset(); + } + + /** + * visit a file and process it + * + * @param vfsStreamFile $file + * @return vfsStreamStructureVisitor + */ + public function visitFile(vfsStreamFile $file) + { + $this->current[$file->getName()] = $file->getContent(); + return $this; + } + + /** + * visit a directory and process it + * + * @param vfsStreamDirectory $dir + * @return vfsStreamStructureVisitor + */ + public function visitDirectory(vfsStreamDirectory $dir) + { + $this->current[$dir->getName()] = array(); + $tmp =& $this->current; + $this->current =& $tmp[$dir->getName()]; + foreach ($dir as $child) { + $this->visit($child); + } + + $this->current =& $tmp; + return $this; + } + + /** + * returns structure of visited contents + * + * @return array + * @api + */ + public function getStructure() + { + return $this->structure; + } + + /** + * resets structure so visitor could be reused + * + * @return vfsStreamStructureVisitor + */ + public function reset() + { + $this->structure = array(); + $this->current =& $this->structure; + return $this; + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamVisitor.php b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamVisitor.php new file mode 100644 index 00000000000..eba73badb29 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/visitor/vfsStreamVisitor.php @@ -0,0 +1,46 @@ + \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/QuotaTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/QuotaTestCase.php new file mode 100644 index 00000000000..7007183e06d --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/QuotaTestCase.php @@ -0,0 +1,81 @@ +quota = new Quota(10); + } + + /** + * @test + */ + public function unlimitedQuotaIsNotLimited() + { + $this->assertFalse(Quota::unlimited()->isLimited()); + } + + /** + * @test + */ + public function limitedQuotaIsLimited() + { + $this->assertTrue($this->quota->isLimited()); + } + + /** + * @test + */ + public function unlimitedQuotaHasAlwaysSpaceLeft() + { + $this->assertEquals(303, Quota::unlimited()->spaceLeft(303)); + } + + /** + * @test + */ + public function hasNoSpaceLeftWhenUsedSpaceIsLargerThanQuota() + { + $this->assertEquals(0, $this->quota->spaceLeft(11)); + } + + /** + * @test + */ + public function hasNoSpaceLeftWhenUsedSpaceIsEqualToQuota() + { + $this->assertEquals(0, $this->quota->spaceLeft(10)); + } + + /** + * @test + */ + public function hasSpaceLeftWhenUsedSpaceIsLowerThanQuota() + { + $this->assertEquals(1, $this->quota->spaceLeft(9)); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/proxy/vfsStreamWrapperRecordingProxy.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/proxy/vfsStreamWrapperRecordingProxy.php new file mode 100644 index 00000000000..899931d6876 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/proxy/vfsStreamWrapperRecordingProxy.php @@ -0,0 +1,326 @@ + + */ + public static function getMethodCalls($path) + { + if (isset(self::$calledMethods[$path]) === true) { + return self::$calledMethods[$path]; + } + + return array(); + } + + /** + * helper method for setting up vfsStream with the proxy + * + * @param string $rootDirName optional name of root directory + * @param int $permissions optional file permissions of root directory + * @return vfsStreamDirectory + * @throws vfsStreamException + */ + public static function setup($rootDirName = 'root', $permissions = null) + { + self::$root = vfsStream::newDirectory($rootDirName, $permissions); + if (true === self::$registered) { + return self::$root; + } + + if (@stream_wrapper_register(vfsStream::SCHEME, __CLASS__) === false) { + throw new vfsStreamException('A handler has already been registered for the ' . vfsStream::SCHEME . ' protocol.'); + } + + self::$registered = true; + return self::$root; + } + + /** + * open the stream + * + * @param string $path the path to open + * @param string $mode mode for opening + * @param string $options options for opening + * @param string $opened_path full path that was actually opened + * @return bool + */ + public function stream_open($path, $mode, $options, $opened_path) + { + $this->path = $path; + self::recordMethodCall('stream_open', $this->path); + return parent::stream_open($path, $mode, $options, $opened_path); + } + + /** + * closes the stream + */ + public function stream_close() + { + self::recordMethodCall('stream_close', $this->path); + return parent::stream_close(); + } + + /** + * read the stream up to $count bytes + * + * @param int $count amount of bytes to read + * @return string + */ + public function stream_read($count) + { + self::recordMethodCall('stream_read', $this->path); + return parent::stream_read($count); + } + + /** + * writes data into the stream + * + * @param string $data + * @return int amount of bytes written + */ + public function stream_write($data) + { + self::recordMethodCall('stream_write', $this->path); + return parent::stream_write($data); + } + + /** + * checks whether stream is at end of file + * + * @return bool + */ + public function stream_eof() + { + self::recordMethodCall('stream_eof', $this->path); + return parent::stream_eof(); + } + + /** + * returns the current position of the stream + * + * @return int + */ + public function stream_tell() + { + self::recordMethodCall('stream_tell', $this->path); + return parent::stream_tell(); + } + + /** + * seeks to the given offset + * + * @param int $offset + * @param int $whence + * @return bool + */ + public function stream_seek($offset, $whence) + { + self::recordMethodCall('stream_seek', $this->path); + return parent::stream_seek($offset, $whence); + } + + /** + * flushes unstored data into storage + * + * @return bool + */ + public function stream_flush() + { + self::recordMethodCall('stream_flush', $this->path); + return parent::stream_flush(); + } + + /** + * returns status of stream + * + * @return array + */ + public function stream_stat() + { + self::recordMethodCall('stream_stat', $this->path); + return parent::stream_stat(); + } + + /** + * retrieve the underlaying resource + * + * @param int $cast_as + * @return bool + */ + public function stream_cast($cast_as) + { + self::recordMethodCall('stream_cast', $this->path); + return parent::stream_cast($cast_as); + } + + /** + * set lock status for stream + * + * @param int $operation + * @return bool + */ + public function stream_lock($operation) + { + self::recordMethodCall('stream_link', $this->path); + return parent::stream_lock($operation); + } + + /** + * remove the data under the given path + * + * @param string $path + * @return bool + */ + public function unlink($path) + { + self::recordMethodCall('unlink', $path); + return parent::unlink($path); + } + + /** + * rename from one path to another + * + * @param string $path_from + * @param string $path_to + * @return bool + */ + public function rename($path_from, $path_to) + { + self::recordMethodCall('rename', $path_from); + return parent::rename($path_from, $path_to); + } + + /** + * creates a new directory + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($path, $mode, $options) + { + self::recordMethodCall('mkdir', $path); + return parent::mkdir($path, $mode, $options); + } + + /** + * removes a directory + * + * @param string $path + * @param int $options + * @return bool + */ + public function rmdir($path, $options) + { + self::recordMethodCall('rmdir', $path); + return parent::rmdir($path, $options); + } + + /** + * opens a directory + * + * @param string $path + * @param int $options + * @return bool + */ + public function dir_opendir($path, $options) + { + $this->path = $path; + self::recordMethodCall('dir_opendir', $this->path); + return parent::dir_opendir($path, $options); + } + + /** + * reads directory contents + * + * @return string + */ + public function dir_readdir() + { + self::recordMethodCall('dir_readdir', $this->path); + return parent::dir_readdir(); + } + + /** + * reset directory iteration + * + * @return bool + */ + public function dir_rewinddir() + { + self::recordMethodCall('dir_rewinddir', $this->path); + return parent::dir_rewinddir(); + } + + /** + * closes directory + * + * @return bool + */ + public function dir_closedir() + { + self::recordMethodCall('dir_closedir', $this->path); + return parent::dir_closedir(); + } + + /** + * returns status of url + * + * @param string $path path of url to return status for + * @param int $flags flags set by the stream API + * @return array + */ + public function url_stat($path, $flags) + { + self::recordMethodCall('url_stat', $path); + return parent::url_stat($path, $flags); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamAbstractContentTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamAbstractContentTestCase.php new file mode 100644 index 00000000000..9bb60795beb --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamAbstractContentTestCase.php @@ -0,0 +1,1054 @@ +assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function executePermissionsForUser() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0100); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function executePermissionsForGroup() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0010); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function executePermissionsForOther() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0001); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function writePermissionsForUser() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0200); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function writePermissionsForGroup() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0020); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function writePermissionsForOther() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0002); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function executeAndWritePermissionsForUser() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0300); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function executeAndWritePermissionsForGroup() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0030); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function executeAndWritePermissionsForOther() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0003); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readPermissionsForUser() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0400); + $this->assertTrue($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readPermissionsForGroup() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0040); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readPermissionsForOther() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0004); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readAndExecutePermissionsForUser() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0500); + $this->assertTrue($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readAndExecutePermissionsForGroup() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0050); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readAndExecutePermissionsForOther() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0005); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readAndWritePermissionsForUser() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0600); + $this->assertTrue($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readAndWritePermissionsForGroup() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0060); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function readAndWritePermissionsForOther() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0006); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function allPermissionsForUser() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0700); + $this->assertTrue($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertTrue($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function allPermissionsForGroup() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0070); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + -1 + ) + ); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function allPermissionsForOther() + { + $abstractContent = new TestvfsStreamAbstractContent('foo', 0007); + $this->assertFalse($abstractContent->isReadable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isReadable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isReadable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isWritable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isWritable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isWritable(-1, + -1 + ) + ); + $this->assertFalse($abstractContent->isExecutable(vfsStream::getCurrentUser(), + vfsStream::getCurrentGroup() + ) + ); + $this->assertFalse($abstractContent->isExecutable(-1, + vfsStream::getCurrentGroup() + ) + ); + $this->assertTrue($abstractContent->isExecutable(-1, + -1 + ) + ); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamContainerIteratorTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamContainerIteratorTestCase.php new file mode 100644 index 00000000000..9aa6960da28 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamContainerIteratorTestCase.php @@ -0,0 +1,55 @@ +getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild1->expects($this->any()) + ->method('getName') + ->will($this->returnValue('bar')); + $dir->addChild($mockChild1); + $mockChild2 = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild2->expects($this->any()) + ->method('getName') + ->will($this->returnValue('baz')); + $dir->addChild($mockChild2); + $dirIterator = $dir->getIterator(); + $this->assertEquals('bar', $dirIterator->key()); + $this->assertTrue($dirIterator->valid()); + $bar = $dirIterator->current(); + $this->assertSame($mockChild1, $bar); + $dirIterator->next(); + $this->assertEquals('baz', $dirIterator->key()); + $this->assertTrue($dirIterator->valid()); + $baz = $dirIterator->current(); + $this->assertSame($mockChild2, $baz); + $dirIterator->next(); + $this->assertFalse($dirIterator->valid()); + $this->assertNull($dirIterator->key()); + $this->assertNull($dirIterator->current()); + $dirIterator->rewind(); + $this->assertTrue($dirIterator->valid()); + $this->assertEquals('bar', $dirIterator->key()); + $bar2 = $dirIterator->current(); + $this->assertSame($mockChild1, $bar2); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamDirectoryIssue18TestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamDirectoryIssue18TestCase.php new file mode 100644 index 00000000000..89cde1ce878 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamDirectoryIssue18TestCase.php @@ -0,0 +1,81 @@ +rootDirectory = vfsStream::newDirectory('/'); + $this->rootDirectory->addChild(vfsStream::newDirectory('var/log/app')); + $dir = $this->rootDirectory->getChild('var/log/app'); + $dir->addChild(vfsStream::newDirectory('app1')); + $dir->addChild(vfsStream::newDirectory('app2')); + $dir->addChild(vfsStream::newDirectory('foo')); + } + + /** + * @test + */ + public function shouldContainThreeSubdirectories() + { + $this->assertEquals(3, + count($this->rootDirectory->getChild('var/log/app')->getChildren()) + ); + } + + /** + * @test + */ + public function shouldContainSubdirectoryFoo() + { + $this->assertTrue($this->rootDirectory->getChild('var/log/app')->hasChild('foo')); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', + $this->rootDirectory->getChild('var/log/app')->getChild('foo') + ); + } + + /** + * @test + */ + public function shouldContainSubdirectoryApp1() + { + $this->assertTrue($this->rootDirectory->getChild('var/log/app')->hasChild('app1')); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', + $this->rootDirectory->getChild('var/log/app')->getChild('app1') + ); + } + + /** + * @test + */ + public function shouldContainSubdirectoryApp2() + { + $this->assertTrue($this->rootDirectory->getChild('var/log/app')->hasChild('app2')); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', + $this->rootDirectory->getChild('var/log/app')->getChild('app2') + ); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamDirectoryTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamDirectoryTestCase.php new file mode 100644 index 00000000000..f8b93842fc1 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamDirectoryTestCase.php @@ -0,0 +1,335 @@ +dir = new vfsStreamDirectory('foo'); + } + + /** + * assure that a directory seperator inside the name throws an exception + * + * @test + * @expectedException org\bovigo\vfs\vfsStreamException + */ + public function invalidCharacterInName() + { + $dir = new vfsStreamDirectory('foo/bar'); + } + + /** + * test default values and methods + * + * @test + */ + public function defaultValues() + { + $this->assertEquals(vfsStreamContent::TYPE_DIR, $this->dir->getType()); + $this->assertEquals('foo', $this->dir->getName()); + $this->assertTrue($this->dir->appliesTo('foo')); + $this->assertTrue($this->dir->appliesTo('foo/bar')); + $this->assertFalse($this->dir->appliesTo('bar')); + $this->assertEquals(array(), $this->dir->getChildren()); + } + + /** + * test renaming the directory + * + * @test + */ + public function rename() + { + $this->dir->rename('bar'); + $this->assertEquals('bar', $this->dir->getName()); + $this->assertFalse($this->dir->appliesTo('foo')); + $this->assertFalse($this->dir->appliesTo('foo/bar')); + $this->assertTrue($this->dir->appliesTo('bar')); + } + + /** + * renaming the directory to an invalid name throws a vfsStreamException + * + * @test + * @expectedException org\bovigo\vfs\vfsStreamException + */ + public function renameToInvalidNameThrowsvfsStreamException() + { + $this->dir->rename('foo/baz'); + } + + /** + * @test + * @since 0.10.0 + */ + public function hasNoChildrenByDefault() + { + $this->assertFalse($this->dir->hasChildren()); + } + + /** + * @test + * @since 0.10.0 + */ + public function hasChildrenReturnsTrueIfAtLeastOneChildPresent() + { + $mockChild = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild->expects($this->any()) + ->method('appliesTo') + ->will($this->returnValue(false)); + $mockChild->expects($this->any()) + ->method('getName') + ->will($this->returnValue('baz')); + $this->dir->addChild($mockChild); + $this->assertTrue($this->dir->hasChildren()); + } + + /** + * @test + */ + public function hasChildReturnsFalseForNonExistingChild() + { + $this->assertFalse($this->dir->hasChild('bar')); + } + + /** + * @test + */ + public function getChildReturnsNullForNonExistingChild() + { + $this->assertNull($this->dir->getChild('bar')); + } + + /** + * @test + */ + public function removeChildReturnsFalseForNonExistingChild() + { + $this->assertFalse($this->dir->removeChild('bar')); + } + + /** + * @test + */ + public function nonExistingChild() + { + $mockChild = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild->expects($this->any()) + ->method('appliesTo') + ->will($this->returnValue(false)); + $mockChild->expects($this->any()) + ->method('getName') + ->will($this->returnValue('baz')); + $this->dir->addChild($mockChild); + $this->assertFalse($this->dir->removeChild('bar')); + } + + /** + * test that adding, handling and removing of a child works as expected + * + * @test + */ + public function childHandling() + { + $mockChild = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild->expects($this->any()) + ->method('getType') + ->will($this->returnValue(vfsStreamContent::TYPE_FILE)); + $mockChild->expects($this->any()) + ->method('getName') + ->will($this->returnValue('bar')); + $mockChild->expects($this->any()) + ->method('appliesTo') + ->with($this->equalTo('bar')) + ->will($this->returnValue(true)); + $mockChild->expects($this->once()) + ->method('size') + ->will($this->returnValue(5)); + $this->dir->addChild($mockChild); + $this->assertTrue($this->dir->hasChild('bar')); + $bar = $this->dir->getChild('bar'); + $this->assertSame($mockChild, $bar); + $this->assertEquals(array($mockChild), $this->dir->getChildren()); + $this->assertEquals(0, $this->dir->size()); + $this->assertEquals(5, $this->dir->sizeSummarized()); + $this->assertTrue($this->dir->removeChild('bar')); + $this->assertEquals(array(), $this->dir->getChildren()); + $this->assertEquals(0, $this->dir->size()); + $this->assertEquals(0, $this->dir->sizeSummarized()); + } + + /** + * test that adding, handling and removing of a child works as expected + * + * @test + */ + public function childHandlingWithSubdirectory() + { + $mockChild = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild->expects($this->any()) + ->method('getType') + ->will($this->returnValue(vfsStreamContent::TYPE_FILE)); + $mockChild->expects($this->any()) + ->method('getName') + ->will($this->returnValue('bar')); + $mockChild->expects($this->once()) + ->method('size') + ->will($this->returnValue(5)); + $subdir = new vfsStreamDirectory('subdir'); + $subdir->addChild($mockChild); + $this->dir->addChild($subdir); + $this->assertTrue($this->dir->hasChild('subdir')); + $this->assertSame($subdir, $this->dir->getChild('subdir')); + $this->assertEquals(array($subdir), $this->dir->getChildren()); + $this->assertEquals(0, $this->dir->size()); + $this->assertEquals(5, $this->dir->sizeSummarized()); + $this->assertTrue($this->dir->removeChild('subdir')); + $this->assertEquals(array(), $this->dir->getChildren()); + $this->assertEquals(0, $this->dir->size()); + $this->assertEquals(0, $this->dir->sizeSummarized()); + } + + /** + * dd + * + * @test + * @group regression + * @group bug_5 + */ + public function addChildReplacesChildWithSameName_Bug_5() + { + $mockChild1 = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild1->expects($this->any()) + ->method('getType') + ->will($this->returnValue(vfsStreamContent::TYPE_FILE)); + $mockChild1->expects($this->any()) + ->method('getName') + ->will($this->returnValue('bar')); + $mockChild2 = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild2->expects($this->any()) + ->method('getType') + ->will($this->returnValue(vfsStreamContent::TYPE_FILE)); + $mockChild2->expects($this->any()) + ->method('getName') + ->will($this->returnValue('bar')); + $this->dir->addChild($mockChild1); + $this->assertTrue($this->dir->hasChild('bar')); + $this->assertSame($mockChild1, $this->dir->getChild('bar')); + $this->dir->addChild($mockChild2); + $this->assertTrue($this->dir->hasChild('bar')); + $this->assertSame($mockChild2, $this->dir->getChild('bar')); + } + + /** + * When testing for a nested path, verify that directory separators are respected properly + * so that subdir1/subdir2 is not considered equal to subdir1Xsubdir2. + * + * @test + * @group bug_24 + * @group regression + */ + public function explicitTestForSeparatorWithNestedPaths_Bug_24() + { + $mockChild = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockChild->expects($this->any()) + ->method('getType') + ->will($this->returnValue(vfsStreamContent::TYPE_FILE)); + $mockChild->expects($this->any()) + ->method('getName') + ->will($this->returnValue('bar')); + + $subdir1 = new vfsStreamDirectory('subdir1'); + $this->dir->addChild($subdir1); + + $subdir2 = new vfsStreamDirectory('subdir2'); + $subdir1->addChild($subdir2); + + $subdir2->addChild($mockChild); + + $this->assertTrue($this->dir->hasChild('subdir1'), "Level 1 path with separator exists"); + $this->assertTrue($this->dir->hasChild('subdir1/subdir2'), "Level 2 path with separator exists"); + $this->assertTrue($this->dir->hasChild('subdir1/subdir2/bar'), "Level 3 path with separator exists"); + $this->assertFalse($this->dir->hasChild('subdir1.subdir2'), "Path with period does not exist"); + $this->assertFalse($this->dir->hasChild('subdir1.subdir2/bar'), "Nested path with period does not exist"); + } + + + /** + * setting and retrieving permissions for a directory + * + * @test + * @group permissions + */ + public function permissions() + { + $this->assertEquals(0777, $this->dir->getPermissions()); + $this->assertSame($this->dir, $this->dir->chmod(0755)); + $this->assertEquals(0755, $this->dir->getPermissions()); + } + + /** + * setting and retrieving permissions for a directory + * + * @test + * @group permissions + */ + public function permissionsSet() + { + $this->dir = new vfsStreamDirectory('foo', 0755); + $this->assertEquals(0755, $this->dir->getPermissions()); + $this->assertSame($this->dir, $this->dir->chmod(0700)); + $this->assertEquals(0700, $this->dir->getPermissions()); + } + + /** + * setting and retrieving owner of a file + * + * @test + * @group permissions + */ + public function owner() + { + $this->assertEquals(vfsStream::getCurrentUser(), $this->dir->getUser()); + $this->assertTrue($this->dir->isOwnedByUser(vfsStream::getCurrentUser())); + $this->assertSame($this->dir, $this->dir->chown(vfsStream::OWNER_USER_1)); + $this->assertEquals(vfsStream::OWNER_USER_1, $this->dir->getUser()); + $this->assertTrue($this->dir->isOwnedByUser(vfsStream::OWNER_USER_1)); + } + + /** + * setting and retrieving owner group of a file + * + * @test + * @group permissions + */ + public function group() + { + $this->assertEquals(vfsStream::getCurrentGroup(), $this->dir->getGroup()); + $this->assertTrue($this->dir->isOwnedByGroup(vfsStream::getCurrentGroup())); + $this->assertSame($this->dir, $this->dir->chgrp(vfsStream::GROUP_USER_1)); + $this->assertEquals(vfsStream::GROUP_USER_1, $this->dir->getGroup()); + $this->assertTrue($this->dir->isOwnedByGroup(vfsStream::GROUP_USER_1)); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamFileTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamFileTestCase.php new file mode 100644 index 00000000000..d93e99424a1 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamFileTestCase.php @@ -0,0 +1,278 @@ +file = new vfsStreamFile('foo'); + } + + /** + * test default values and methods + * + * @test + */ + public function defaultValues() + { + $this->assertEquals(vfsStreamContent::TYPE_FILE, $this->file->getType()); + $this->assertEquals('foo', $this->file->getName()); + $this->assertTrue($this->file->appliesTo('foo')); + $this->assertFalse($this->file->appliesTo('foo/bar')); + $this->assertFalse($this->file->appliesTo('bar')); + } + + /** + * test setting and getting the content of a file + * + * @test + */ + public function content() + { + $this->assertNull($this->file->getContent()); + $this->assertSame($this->file, $this->file->setContent('bar')); + $this->assertEquals('bar', $this->file->getContent()); + $this->assertSame($this->file, $this->file->withContent('baz')); + $this->assertEquals('baz', $this->file->getContent()); + } + + /** + * test renaming the directory + * + * @test + */ + public function rename() + { + $this->file->rename('bar'); + $this->assertEquals('bar', $this->file->getName()); + $this->assertFalse($this->file->appliesTo('foo')); + $this->assertFalse($this->file->appliesTo('foo/bar')); + $this->assertTrue($this->file->appliesTo('bar')); + } + + /** + * test reading contents from the file + * + * @test + */ + public function readEmptyFile() + { + $this->assertTrue($this->file->eof()); + $this->assertEquals(0, $this->file->size()); + $this->assertEquals('', $this->file->read(5)); + $this->assertEquals(5, $this->file->getBytesRead()); + $this->assertTrue($this->file->eof()); + } + + /** + * test reading contents from the file + * + * @test + */ + public function read() + { + $this->file->setContent('foobarbaz'); + $this->assertFalse($this->file->eof()); + $this->assertEquals(9, $this->file->size()); + $this->assertEquals('foo', $this->file->read(3)); + $this->assertEquals(3, $this->file->getBytesRead()); + $this->assertFalse($this->file->eof()); + $this->assertEquals(9, $this->file->size()); + $this->assertEquals('bar', $this->file->read(3)); + $this->assertEquals(6, $this->file->getBytesRead()); + $this->assertFalse($this->file->eof()); + $this->assertEquals(9, $this->file->size()); + $this->assertEquals('baz', $this->file->read(3)); + $this->assertEquals(9, $this->file->getBytesRead()); + $this->assertEquals(9, $this->file->size()); + $this->assertTrue($this->file->eof()); + $this->assertEquals('', $this->file->read(3)); + } + + /** + * test seeking to offset + * + * @test + */ + public function seekEmptyFile() + { + $this->assertFalse($this->file->seek(0, 55)); + $this->assertTrue($this->file->seek(0, SEEK_SET)); + $this->assertEquals(0, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(5, SEEK_SET)); + $this->assertEquals(5, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(0, SEEK_CUR)); + $this->assertEquals(5, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(2, SEEK_CUR)); + $this->assertEquals(7, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(0, SEEK_END)); + $this->assertEquals(0, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(2, SEEK_END)); + $this->assertEquals(2, $this->file->getBytesRead()); + } + + /** + * test seeking to offset + * + * @test + */ + public function seekRead() + { + $this->file->setContent('foobarbaz'); + $this->assertFalse($this->file->seek(0, 55)); + $this->assertTrue($this->file->seek(0, SEEK_SET)); + $this->assertEquals('foobarbaz', $this->file->readUntilEnd()); + $this->assertEquals(0, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(5, SEEK_SET)); + $this->assertEquals('rbaz', $this->file->readUntilEnd()); + $this->assertEquals(5, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(0, SEEK_CUR)); + $this->assertEquals('rbaz', $this->file->readUntilEnd()); + $this->assertEquals(5, $this->file->getBytesRead(), 5); + $this->assertTrue($this->file->seek(2, SEEK_CUR)); + $this->assertEquals('az', $this->file->readUntilEnd()); + $this->assertEquals(7, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(0, SEEK_END)); + $this->assertEquals('', $this->file->readUntilEnd()); + $this->assertEquals(9, $this->file->getBytesRead()); + $this->assertTrue($this->file->seek(2, SEEK_END)); + $this->assertEquals('', $this->file->readUntilEnd()); + $this->assertEquals(11, $this->file->getBytesRead()); + } + + /** + * test writing data into the file + * + * @test + */ + public function writeEmptyFile() + { + $this->assertEquals(3, $this->file->write('foo')); + $this->assertEquals('foo', $this->file->getContent()); + $this->assertEquals(3, $this->file->size()); + $this->assertEquals(3, $this->file->write('bar')); + $this->assertEquals('foobar', $this->file->getContent()); + $this->assertEquals(6, $this->file->size()); + } + + /** + * test writing data into the file + * + * @test + */ + public function write() + { + $this->file->setContent('foobarbaz'); + $this->assertTrue($this->file->seek(3, SEEK_SET)); + $this->assertEquals(3, $this->file->write('foo')); + $this->assertEquals('foofoobaz', $this->file->getContent()); + $this->assertEquals(9, $this->file->size()); + $this->assertEquals(3, $this->file->write('bar')); + $this->assertEquals('foofoobar', $this->file->getContent()); + $this->assertEquals(9, $this->file->size()); + } + + /** + * setting and retrieving permissions for a file + * + * @test + * @group permissions + */ + public function permissions() + { + $this->assertEquals(0666, $this->file->getPermissions()); + $this->assertSame($this->file, $this->file->chmod(0644)); + $this->assertEquals(0644, $this->file->getPermissions()); + } + + /** + * setting and retrieving permissions for a file + * + * @test + * @group permissions + */ + public function permissionsSet() + { + $this->file = new vfsStreamFile('foo', 0644); + $this->assertEquals(0644, $this->file->getPermissions()); + $this->assertSame($this->file, $this->file->chmod(0600)); + $this->assertEquals(0600, $this->file->getPermissions()); + } + + /** + * setting and retrieving owner of a file + * + * @test + * @group permissions + */ + public function owner() + { + $this->assertEquals(vfsStream::getCurrentUser(), $this->file->getUser()); + $this->assertTrue($this->file->isOwnedByUser(vfsStream::getCurrentUser())); + $this->assertSame($this->file, $this->file->chown(vfsStream::OWNER_USER_1)); + $this->assertEquals(vfsStream::OWNER_USER_1, $this->file->getUser()); + $this->assertTrue($this->file->isOwnedByUser(vfsStream::OWNER_USER_1)); + } + + /** + * setting and retrieving owner group of a file + * + * @test + * @group permissions + */ + public function group() + { + $this->assertEquals(vfsStream::getCurrentGroup(), $this->file->getGroup()); + $this->assertTrue($this->file->isOwnedByGroup(vfsStream::getCurrentGroup())); + $this->assertSame($this->file, $this->file->chgrp(vfsStream::GROUP_USER_1)); + $this->assertEquals(vfsStream::GROUP_USER_1, $this->file->getGroup()); + $this->assertTrue($this->file->isOwnedByGroup(vfsStream::GROUP_USER_1)); + } + + /** + * @test + * @group issue_33 + * @since 1.1.0 + */ + public function truncateRemovesSuperflouosContent() + { + $this->assertEquals(11, $this->file->write("lorem ipsum")); + $this->assertTrue($this->file->truncate(5)); + $this->assertEquals(5, $this->file->size()); + $this->assertEquals('lorem', $this->file->getContent()); + } + + /** + * @test + * @group issue_33 + * @since 1.1.0 + */ + public function truncateToGreaterSizeAddsZeroBytes() + { + $this->assertEquals(11, $this->file->write("lorem ipsum")); + $this->assertTrue($this->file->truncate(25)); + $this->assertEquals(25, $this->file->size()); + $this->assertEquals("lorem ipsum\0\0\0\0\0\0\0\0\0\0\0\0\0\0", $this->file->getContent()); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamGlobTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamGlobTestCase.php new file mode 100644 index 00000000000..24884edf62e --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamGlobTestCase.php @@ -0,0 +1,29 @@ +assertEmpty(glob(vfsStream::url('example'), GLOB_MARK)); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamResolveIncludePathTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamResolveIncludePathTestCase.php new file mode 100644 index 00000000000..a4d70b54ec2 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamResolveIncludePathTestCase.php @@ -0,0 +1,62 @@ +backupIncludePath = get_include_path(); + vfsStream::setup(); + mkdir('vfs://root/a/path', 0777, true); + set_include_path('vfs://root/a' . PATH_SEPARATOR . $this->backupIncludePath); + } + + /** + * clean up test environment + */ + public function tearDown() + { + set_include_path($this->backupIncludePath); + } + + /** + * @test + */ + public function knownFileCanBeResolved() + { + file_put_contents('vfs://root/a/path/knownFile.php', ''); + $this->assertEquals('vfs://root/a/path/knownFile.php', stream_resolve_include_path('path/knownFile.php')); + } + + /** + * @test + */ + public function unknownFileCanNotBeResolvedYieldsFalse() + { + $this->assertFalse(@stream_resolve_include_path('path/unknownFile.php')); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamTestCase.php new file mode 100644 index 00000000000..32101160c4f --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamTestCase.php @@ -0,0 +1,707 @@ +assertEquals('vfs://foo', vfsStream::url('foo')); + $this->assertEquals('vfs://foo/bar.baz', vfsStream::url('foo/bar.baz')); + $this->assertEquals('vfs://foo/bar.baz', vfsStream::url('foo\bar.baz')); + } + + /** + * assure that url2path conversion works correct + * + * @test + */ + public function path() + { + $this->assertEquals('foo', vfsStream::path('vfs://foo')); + $this->assertEquals('foo/bar.baz', vfsStream::path('vfs://foo/bar.baz')); + $this->assertEquals('foo/bar.baz', vfsStream::path('vfs://foo\bar.baz')); + } + + /** + * windows directory separators are converted into default separator + * + * @author Gabriel Birke + * @test + */ + public function pathConvertsWindowsDirectorySeparators() + { + $this->assertEquals('foo/bar', vfsStream::path('vfs://foo\\bar')); + } + + /** + * trailing whitespace should be removed + * + * @author Gabriel Birke + * @test + */ + public function pathRemovesTrailingWhitespace() + { + $this->assertEquals('foo/bar', vfsStream::path('vfs://foo/bar ')); + } + + /** + * trailing slashes are removed + * + * @author Gabriel Birke + * @test + */ + public function pathRemovesTrailingSlash() + { + $this->assertEquals('foo/bar', vfsStream::path('vfs://foo/bar/')); + } + + /** + * trailing slash and whitespace should be removed + * + * @author Gabriel Birke + * @test + */ + public function pathRemovesTrailingSlashAndWhitespace() + { + $this->assertEquals('foo/bar', vfsStream::path('vfs://foo/bar/ ')); + } + + /** + * double slashes should be replaced by single slash + * + * @author Gabriel Birke + * @test + */ + public function pathRemovesDoubleSlashes() + { + // Regular path + $this->assertEquals('my/path', vfsStream::path('vfs://my/path')); + // Path with double slashes + $this->assertEquals('my/path', vfsStream::path('vfs://my//path')); + } + + /** + * test to create a new file + * + * @test + */ + public function newFile() + { + $file = vfsStream::newFile('filename.txt'); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamFile', $file); + $this->assertEquals('filename.txt', $file->getName()); + $this->assertEquals(0666, $file->getPermissions()); + } + + /** + * test to create a new file with non-default permissions + * + * @test + * @group permissions + */ + public function newFileWithDifferentPermissions() + { + $file = vfsStream::newFile('filename.txt', 0644); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamFile', $file); + $this->assertEquals('filename.txt', $file->getName()); + $this->assertEquals(0644, $file->getPermissions()); + } + + /** + * test to create a new directory structure + * + * @test + */ + public function newSingleDirectory() + { + $foo = vfsStream::newDirectory('foo'); + $this->assertEquals('foo', $foo->getName()); + $this->assertEquals(0, count($foo->getChildren())); + $this->assertEquals(0777, $foo->getPermissions()); + } + + /** + * test to create a new directory structure with non-default permissions + * + * @test + * @group permissions + */ + public function newSingleDirectoryWithDifferentPermissions() + { + $foo = vfsStream::newDirectory('foo', 0755); + $this->assertEquals('foo', $foo->getName()); + $this->assertEquals(0, count($foo->getChildren())); + $this->assertEquals(0755, $foo->getPermissions()); + } + + /** + * test to create a new directory structure + * + * @test + */ + public function newDirectoryStructure() + { + $foo = vfsStream::newDirectory('foo/bar/baz'); + $this->assertEquals('foo', $foo->getName()); + $this->assertEquals(0777, $foo->getPermissions()); + $this->assertTrue($foo->hasChild('bar')); + $this->assertTrue($foo->hasChild('bar/baz')); + $this->assertFalse($foo->hasChild('baz')); + $bar = $foo->getChild('bar'); + $this->assertEquals('bar', $bar->getName()); + $this->assertEquals(0777, $bar->getPermissions()); + $this->assertTrue($bar->hasChild('baz')); + $baz1 = $bar->getChild('baz'); + $this->assertEquals('baz', $baz1->getName()); + $this->assertEquals(0777, $baz1->getPermissions()); + $baz2 = $foo->getChild('bar/baz'); + $this->assertSame($baz1, $baz2); + } + + /** + * test that correct directory structure is created + * + * @test + */ + public function newDirectoryWithSlashAtStart() + { + $foo = vfsStream::newDirectory('/foo/bar/baz', 0755); + $this->assertEquals('foo', $foo->getName()); + $this->assertEquals(0755, $foo->getPermissions()); + $this->assertTrue($foo->hasChild('bar')); + $this->assertTrue($foo->hasChild('bar/baz')); + $this->assertFalse($foo->hasChild('baz')); + $bar = $foo->getChild('bar'); + $this->assertEquals('bar', $bar->getName()); + $this->assertEquals(0755, $bar->getPermissions()); + $this->assertTrue($bar->hasChild('baz')); + $baz1 = $bar->getChild('baz'); + $this->assertEquals('baz', $baz1->getName()); + $this->assertEquals(0755, $baz1->getPermissions()); + $baz2 = $foo->getChild('bar/baz'); + $this->assertSame($baz1, $baz2); + } + + /** + * @test + * @group setup + * @since 0.7.0 + */ + public function setupRegistersStreamWrapperAndCreatesRootDirectoryWithDefaultNameAndPermissions() + { + $root = vfsStream::setup(); + $this->assertSame($root, vfsStreamWrapper::getRoot()); + $this->assertEquals('root', $root->getName()); + $this->assertEquals(0777, $root->getPermissions()); + } + + /** + * @test + * @group setup + * @since 0.7.0 + */ + public function setupRegistersStreamWrapperAndCreatesRootDirectoryWithGivenNameAndDefaultPermissions() + { + $root = vfsStream::setup('foo'); + $this->assertSame($root, vfsStreamWrapper::getRoot()); + $this->assertEquals('foo', $root->getName()); + $this->assertEquals(0777, $root->getPermissions()); + } + + /** + * @test + * @group setup + * @since 0.7.0 + */ + public function setupRegistersStreamWrapperAndCreatesRootDirectoryWithGivenNameAndPermissions() + { + $root = vfsStream::setup('foo', 0444); + $this->assertSame($root, vfsStreamWrapper::getRoot()); + $this->assertEquals('foo', $root->getName()); + $this->assertEquals(0444, $root->getPermissions()); + } + + /** + * @test + * @group issue_14 + * @group issue_20 + * @since 0.10.0 + */ + public function setupWithEmptyArrayIsEqualToSetup() + { + $root = vfsStream::setup('example', + 0755, + array() + ); + $this->assertEquals('example', $root->getName()); + $this->assertEquals(0755, $root->getPermissions()); + $this->assertFalse($root->hasChildren()); + } + + /** + * @test + * @group issue_14 + * @group issue_20 + * @since 0.10.0 + */ + public function setupArraysAreTurnedIntoSubdirectories() + { + $root = vfsStream::setup('root', + null, + array('test' => array()) + ); + $this->assertTrue($root->hasChildren()); + $this->assertTrue($root->hasChild('test')); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', + $root->getChild('test') + ); + $this->assertFalse($root->getChild('test')->hasChildren()); + } + + /** + * @test + * @group issue_14 + * @group issue_20 + * @since 0.10.0 + */ + public function setupStringsAreTurnedIntoFilesWithContent() + { + $root = vfsStream::setup('root', + null, + array('test.txt' => 'some content') + ); + $this->assertTrue($root->hasChildren()); + $this->assertTrue($root->hasChild('test.txt')); + $this->assertVfsFile($root->getChild('test.txt'), 'some content'); + } + + /** + * @test + * @group issue_14 + * @group issue_20 + * @since 0.10.0 + */ + public function setupWorksRecursively() + { + $root = vfsStream::setup('root', + null, + array('test' => array('foo' => array('test.txt' => 'hello'), + 'baz.txt' => 'world' + ) + ) + ); + $this->assertTrue($root->hasChildren()); + $this->assertTrue($root->hasChild('test')); + $test = $root->getChild('test'); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', $test); + $this->assertTrue($test->hasChildren()); + $this->assertTrue($test->hasChild('baz.txt')); + $this->assertVfsFile($test->getChild('baz.txt'), 'world'); + + $this->assertTrue($test->hasChild('foo')); + $foo = $test->getChild('foo'); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', $foo); + $this->assertTrue($foo->hasChildren()); + $this->assertTrue($foo->hasChild('test.txt')); + $this->assertVfsFile($foo->getChild('test.txt'), 'hello'); + } + + /** + * @test + * @group issue_17 + * @group issue_20 + */ + public function setupCastsNumericDirectoriesToStrings() + { + $root = vfsStream::setup('root', + null, + array(2011 => array ('test.txt' => 'some content')) + ); + $this->assertTrue($root->hasChild('2011')); + + $directory = $root->getChild('2011'); + $this->assertVfsFile($directory->getChild('test.txt'), 'some content'); + + $this->assertTrue(file_exists('vfs://2011/test.txt')); + } + + /** + * @test + * @group issue_20 + * @since 0.11.0 + */ + public function createArraysAreTurnedIntoSubdirectories() + { + $baseDir = vfsStream::create(array('test' => array()), new vfsStreamDirectory('baseDir')); + $this->assertTrue($baseDir->hasChildren()); + $this->assertTrue($baseDir->hasChild('test')); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', + $baseDir->getChild('test') + ); + $this->assertFalse($baseDir->getChild('test')->hasChildren()); + } + + /** + * @test + * @group issue_20 + * @since 0.11.0 + */ + public function createArraysAreTurnedIntoSubdirectoriesOfRoot() + { + $root = vfsStream::setup(); + $this->assertSame($root, vfsStream::create(array('test' => array()))); + $this->assertTrue($root->hasChildren()); + $this->assertTrue($root->hasChild('test')); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', + $root->getChild('test') + ); + $this->assertFalse($root->getChild('test')->hasChildren()); + } + + /** + * @test + * @group issue_20 + * @expectedException \InvalidArgumentException + * @since 0.11.0 + */ + public function createThrowsExceptionIfNoBaseDirGivenAndNoRootSet() + { + vfsStream::create(array('test' => array())); + } + + /** + * @test + * @group issue_20 + * @since 0.11.0 + */ + public function createWorksRecursively() + { + $baseDir = vfsStream::create(array('test' => array('foo' => array('test.txt' => 'hello'), + 'baz.txt' => 'world' + ) + ), + new vfsStreamDirectory('baseDir') + ); + $this->assertTrue($baseDir->hasChildren()); + $this->assertTrue($baseDir->hasChild('test')); + $test = $baseDir->getChild('test'); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', $test); + $this->assertTrue($test->hasChildren()); + $this->assertTrue($test->hasChild('baz.txt')); + $this->assertVfsFile($test->getChild('baz.txt'), 'world'); + + $this->assertTrue($test->hasChild('foo')); + $foo = $test->getChild('foo'); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', $foo); + $this->assertTrue($foo->hasChildren()); + $this->assertTrue($foo->hasChild('test.txt')); + $this->assertVfsFile($foo->getChild('test.txt'), 'hello'); + } + + /** + * @test + * @group issue_20 + * @since 0.11.0 + */ + public function createWorksRecursivelyWithRoot() + { + $root = vfsStream::setup(); + $this->assertSame($root, + vfsStream::create(array('test' => array('foo' => array('test.txt' => 'hello'), + 'baz.txt' => 'world' + ) + ) + ) + ); + $this->assertTrue($root->hasChildren()); + $this->assertTrue($root->hasChild('test')); + $test = $root->getChild('test'); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', $test); + $this->assertTrue($test->hasChildren()); + $this->assertTrue($test->hasChild('baz.txt')); + $this->assertVfsFile($test->getChild('baz.txt'), 'world'); + + $this->assertTrue($test->hasChild('foo')); + $foo = $test->getChild('foo'); + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamDirectory', $foo); + $this->assertTrue($foo->hasChildren()); + $this->assertTrue($foo->hasChild('test.txt')); + $this->assertVfsFile($foo->getChild('test.txt'), 'hello'); + } + + /** + * @test + * @group issue_20 + * @since 0.10.0 + */ + public function createStringsAreTurnedIntoFilesWithContent() + { + $baseDir = vfsStream::create(array('test.txt' => 'some content'), new vfsStreamDirectory('baseDir')); + $this->assertTrue($baseDir->hasChildren()); + $this->assertTrue($baseDir->hasChild('test.txt')); + $this->assertVfsFile($baseDir->getChild('test.txt'), 'some content'); + } + + /** + * @test + * @group issue_20 + * @since 0.11.0 + */ + public function createStringsAreTurnedIntoFilesWithContentWithRoot() + { + $root = vfsStream::setup(); + $this->assertSame($root, + vfsStream::create(array('test.txt' => 'some content')) + ); + $this->assertTrue($root->hasChildren()); + $this->assertTrue($root->hasChild('test.txt')); + $this->assertVfsFile($root->getChild('test.txt'), 'some content'); + } + + /** + * @test + * @group issue_20 + * @since 0.11.0 + */ + public function createCastsNumericDirectoriesToStrings() + { + $baseDir = vfsStream::create(array(2011 => array ('test.txt' => 'some content')), new vfsStreamDirectory('baseDir')); + $this->assertTrue($baseDir->hasChild('2011')); + + $directory = $baseDir->getChild('2011'); + $this->assertVfsFile($directory->getChild('test.txt'), 'some content'); + } + + /** + * @test + * @group issue_20 + * @since 0.11.0 + */ + public function createCastsNumericDirectoriesToStringsWithRoot() + { + $root = vfsStream::setup(); + $this->assertSame($root, + vfsStream::create(array(2011 => array ('test.txt' => 'some content'))) + ); + $this->assertTrue($root->hasChild('2011')); + + $directory = $root->getChild('2011'); + $this->assertVfsFile($directory->getChild('test.txt'), 'some content'); + } + + /** + * helper function for assertions on vfsStreamFile + * + * @param vfsStreamFile $file + * @param string $content + */ + protected function assertVfsFile(vfsStreamFile $file, $content) + { + $this->assertInstanceOf('org\\bovigo\\vfs\\vfsStreamFile', + $file + ); + $this->assertEquals($content, + $file->getContent() + ); + } + + /** + * @test + * @group issue_10 + * @since 0.10.0 + */ + public function inspectWithContentGivesContentToVisitor() + { + $mockContent = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockVisitor = $this->getMock('org\\bovigo\\vfs\\visitor\\vfsStreamVisitor'); + $mockVisitor->expects($this->once()) + ->method('visit') + ->with($this->equalTo($mockContent)) + ->will($this->returnValue($mockVisitor)); + $this->assertSame($mockVisitor, vfsStream::inspect($mockVisitor, $mockContent)); + } + + /** + * @test + * @group issue_10 + * @since 0.10.0 + */ + public function inspectWithoutContentGivesRootToVisitor() + { + $root = vfsStream::setup(); + $mockVisitor = $this->getMock('org\\bovigo\\vfs\\visitor\\vfsStreamVisitor'); + $mockVisitor->expects($this->once()) + ->method('visitDirectory') + ->with($this->equalTo($root)) + ->will($this->returnValue($mockVisitor)); + $this->assertSame($mockVisitor, vfsStream::inspect($mockVisitor)); + } + + /** + * @test + * @group issue_10 + * @expectedException \InvalidArgumentException + * @since 0.10.0 + */ + public function inspectWithoutContentAndWithoutRootThrowsInvalidArgumentException() + { + $mockVisitor = $this->getMock('org\\bovigo\\vfs\\visitor\\vfsStreamVisitor'); + $mockVisitor->expects($this->never()) + ->method('visit'); + $mockVisitor->expects($this->never()) + ->method('visitDirectory'); + vfsStream::inspect($mockVisitor); + } + + /** + * returns path to file system copy resource directory + * + * @return string + */ + protected function getFileSystemCopyDir() + { + return realpath(dirname(__FILE__) . '/../../../../resources/filesystemcopy'); + } + + /** + * @test + * @group issue_4 + * @expectedException \InvalidArgumentException + * @since 0.11.0 + */ + public function copyFromFileSystemThrowsExceptionIfNoBaseDirGivenAndNoRootSet() + { + vfsStream::copyFromFileSystem($this->getFileSystemCopyDir()); + } + + /** + * @test + * @group issue_4 + * @since 0.11.0 + */ + public function copyFromEmptyFolder() + { + $baseDir = vfsStream::copyFromFileSystem($this->getFileSystemCopyDir() . '/emptyFolder', + vfsStream::newDirectory('test') + ); + $baseDir->removeChild('.gitignore'); + $this->assertFalse($baseDir->hasChildren()); + } + + /** + * @test + * @group issue_4 + * @since 0.11.0 + */ + public function copyFromEmptyFolderWithRoot() + { + $root = vfsStream::setup(); + $this->assertEquals($root, + vfsStream::copyFromFileSystem($this->getFileSystemCopyDir() . '/emptyFolder') + ); + $root->removeChild('.gitignore'); + $this->assertFalse($root->hasChildren()); + } + + /** + * @test + * @group issue_4 + * @since 0.11.0 + */ + public function copyFromWithSubFolders() + { + $baseDir = vfsStream::copyFromFileSystem($this->getFileSystemCopyDir(), + vfsStream::newDirectory('test'), + 3 + ); + $this->assertTrue($baseDir->hasChildren()); + $this->assertTrue($baseDir->hasChild('emptyFolder')); + $this->assertTrue($baseDir->hasChild('withSubfolders')); + $subfolderDir = $baseDir->getChild('withSubfolders'); + $this->assertTrue($subfolderDir->hasChild('subfolder1')); + $this->assertTrue($subfolderDir->getChild('subfolder1')->hasChild('file1.txt')); + $this->assertVfsFile($subfolderDir->getChild('subfolder1/file1.txt'), ''); + $this->assertTrue($subfolderDir->hasChild('subfolder2')); + $this->assertTrue($subfolderDir->hasChild('aFile.txt')); + $this->assertVfsFile($subfolderDir->getChild('aFile.txt'), 'foo'); + } + + /** + * @test + * @group issue_4 + * @since 0.11.0 + */ + public function copyFromWithSubFoldersWithRoot() + { + $root = vfsStream::setup(); + $this->assertEquals($root, + vfsStream::copyFromFileSystem($this->getFileSystemCopyDir(), + null, + 3 + ) + ); + $this->assertTrue($root->hasChildren()); + $this->assertTrue($root->hasChild('emptyFolder')); + $this->assertTrue($root->hasChild('withSubfolders')); + $subfolderDir = $root->getChild('withSubfolders'); + $this->assertTrue($subfolderDir->hasChild('subfolder1')); + $this->assertTrue($subfolderDir->getChild('subfolder1')->hasChild('file1.txt')); + $this->assertVfsFile($subfolderDir->getChild('subfolder1/file1.txt'), ''); + $this->assertTrue($subfolderDir->hasChild('subfolder2')); + $this->assertTrue($subfolderDir->hasChild('aFile.txt')); + $this->assertVfsFile($subfolderDir->getChild('aFile.txt'), 'foo'); + } + + /** + * @test + * @group issue_4 + * @group issue_29 + * @since 0.11.2 + */ + public function copyFromPreservesFilePermissions() + { + if (DIRECTORY_SEPARATOR !== '/') { + $this->markTestSkipped('Only applicable on Linux style systems.'); + } + + $copyDir = $this->getFileSystemCopyDir(); + $root = vfsStream::setup(); + $this->assertEquals($root, + vfsStream::copyFromFileSystem($copyDir, + null + ) + ); + $this->assertEquals(fileperms($copyDir . '/withSubfolders') - vfsStreamContent::TYPE_DIR, + $root->getChild('withSubfolders') + ->getPermissions() + ); + $this->assertEquals(fileperms($copyDir . '/withSubfolders/aFile.txt') - vfsStreamContent::TYPE_FILE, + $root->getChild('withSubfolders/aFile.txt') + ->getPermissions() + ); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamUmaskTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamUmaskTestCase.php new file mode 100644 index 00000000000..342af310b84 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamUmaskTestCase.php @@ -0,0 +1,195 @@ +assertEquals(vfsStream::umask(), + vfsStream::umask() + ); + $this->assertEquals(0000, + vfsStream::umask() + ); + } + + /** + * @test + */ + public function changingUmaskSettingReturnsOldUmaskSetting() + { + $this->assertEquals(0000, + vfsStream::umask(0022) + ); + $this->assertEquals(0022, + vfsStream::umask() + ); + } + + /** + * @test + */ + public function createFileWithDefaultUmaskSetting() + { + $file = new vfsStreamFile('foo'); + $this->assertEquals(0666, $file->getPermissions()); + } + + /** + * @test + */ + public function createFileWithDifferentUmaskSetting() + { + vfsStream::umask(0022); + $file = new vfsStreamFile('foo'); + $this->assertEquals(0644, $file->getPermissions()); + } + + /** + * @test + */ + public function createDirectoryWithDefaultUmaskSetting() + { + $directory = new vfsStreamDirectory('foo'); + $this->assertEquals(0777, $directory->getPermissions()); + } + + /** + * @test + */ + public function createDirectoryWithDifferentUmaskSetting() + { + vfsStream::umask(0022); + $directory = new vfsStreamDirectory('foo'); + $this->assertEquals(0755, $directory->getPermissions()); + } + + /** + * @test + */ + public function createFileUsingStreamWithDefaultUmaskSetting() + { + $root = vfsStream::setup(); + file_put_contents(vfsStream::url('root/newfile.txt'), 'file content'); + $this->assertEquals(0666, $root->getChild('newfile.txt')->getPermissions()); + } + + /** + * @test + */ + public function createFileUsingStreamWithDifferentUmaskSetting() + { + $root = vfsStream::setup(); + vfsStream::umask(0022); + file_put_contents(vfsStream::url('root/newfile.txt'), 'file content'); + $this->assertEquals(0644, $root->getChild('newfile.txt')->getPermissions()); + } + + /** + * @test + */ + public function createDirectoryUsingStreamWithDefaultUmaskSetting() + { + $root = vfsStream::setup(); + mkdir(vfsStream::url('root/newdir')); + $this->assertEquals(0777, $root->getChild('newdir')->getPermissions()); + } + + /** + * @test + */ + public function createDirectoryUsingStreamWithDifferentUmaskSetting() + { + $root = vfsStream::setup(); + vfsStream::umask(0022); + mkdir(vfsStream::url('root/newdir')); + $this->assertEquals(0755, $root->getChild('newdir')->getPermissions()); + } + + /** + * @test + */ + public function createDirectoryUsingStreamWithExplicit0() + { + $root = vfsStream::setup(); + vfsStream::umask(0022); + mkdir(vfsStream::url('root/newdir'), null); + $this->assertEquals(0000, $root->getChild('newdir')->getPermissions()); + } + + /** + * @test + * + */ + public function createDirectoryUsingStreamWithDifferentUmaskSettingButExplicit0777() + { + $root = vfsStream::setup(); + vfsStream::umask(0022); + mkdir(vfsStream::url('root/newdir'), 0777); + $this->assertEquals(0755, $root->getChild('newdir')->getPermissions()); + } + + /** + * @test + */ + public function createDirectoryUsingStreamWithDifferentUmaskSettingButExplicitModeRequestedByCall() + { + $root = vfsStream::setup(); + vfsStream::umask(0022); + mkdir(vfsStream::url('root/newdir'), 0700); + $this->assertEquals(0700, $root->getChild('newdir')->getPermissions()); + } + + /** + * @test + */ + public function defaultUmaskSettingDoesNotInfluenceSetup() + { + $root = vfsStream::setup(); + $this->assertEquals(0777, $root->getPermissions()); + } + + /** + * @test + */ + public function umaskSettingShouldBeRespectedBySetup() + { + vfsStream::umask(0022); + $root = vfsStream::setup(); + $this->assertEquals(0755, $root->getPermissions()); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperAlreadyRegisteredTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperAlreadyRegisteredTestCase.php new file mode 100644 index 00000000000..c7f78dcc800 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperAlreadyRegisteredTestCase.php @@ -0,0 +1,63 @@ +getMock('org\\bovigo\\vfs\\vfsStreamWrapper'); + stream_wrapper_register(vfsStream::SCHEME, get_class($mock)); + } + + /** + * clean up test environment + */ + public function tearDown() + { + TestvfsStreamWrapper::unregister(); + } + + /** + * registering the stream wrapper when another stream wrapper is already + * registered for the vfs scheme should throw an exception + * + * @test + * @expectedException org\bovigo\vfs\vfsStreamException + */ + public function registerOverAnotherStreamWrapper() + { + vfsStreamWrapper::register(); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperBaseTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperBaseTestCase.php new file mode 100644 index 00000000000..52ec40cf7ea --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperBaseTestCase.php @@ -0,0 +1,99 @@ +fooURL = vfsStream::url('foo'); + $this->barURL = vfsStream::url('foo/bar'); + $this->baz1URL = vfsStream::url('foo/bar/baz1'); + $this->baz2URL = vfsStream::url('foo/baz2'); + $this->foo = new vfsStreamDirectory('foo'); + $this->bar = new vfsStreamDirectory('bar'); + $this->baz1 = vfsStream::newFile('baz1') + ->lastModified(300) + ->lastAccessed(300) + ->lastAttributeModified(300) + ->withContent('baz 1'); + $this->baz2 = vfsStream::newFile('baz2') + ->withContent('baz2') + ->lastModified(400) + ->lastAccessed(400) + ->lastAttributeModified(400); + $this->bar->addChild($this->baz1); + $this->foo->addChild($this->bar); + $this->foo->addChild($this->baz2); + $this->foo->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + $this->bar->lastModified(200) + ->lastAccessed(100) + ->lastAttributeModified(100); + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot($this->foo); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperDirSeparatorTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperDirSeparatorTestCase.php new file mode 100644 index 00000000000..f08f0a7fde3 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperDirSeparatorTestCase.php @@ -0,0 +1,57 @@ +root = vfsStream::setup(); + } + + /** + * @test + */ + public function fileCanBeAccessedUsingWinDirSeparator() + { + vfsStream::newFile('foo/bar/baz.txt') + ->at($this->root) + ->withContent('test'); + $this->assertEquals('test', file_get_contents('vfs://root/foo\bar\baz.txt')); + } + + + /** + * @test + */ + public function directoryCanBeCreatedUsingWinDirSeparator() + { + mkdir('vfs://root/dir\bar\foo', true, 0777); + $this->assertTrue($this->root->hasChild('dir')); + $this->assertTrue($this->root->getChild('dir')->hasChild('bar')); + $this->assertTrue($this->root->getChild('dir/bar')->hasChild('foo')); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperDirTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperDirTestCase.php new file mode 100644 index 00000000000..cf4efdd4a9a --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperDirTestCase.php @@ -0,0 +1,617 @@ +assertFalse(mkdir(vfsStream::url('another'))); + $this->assertEquals(2, count($this->foo->getChildren())); + $this->assertSame($this->foo, vfsStreamWrapper::getRoot()); + } + + /** + * mkdir() should not overwrite existing root + * + * @test + */ + public function mkdirNoNewRootRecursively() + { + $this->assertFalse(mkdir(vfsStream::url('another/more'), 0777, true)); + $this->assertEquals(2, count($this->foo->getChildren())); + $this->assertSame($this->foo, vfsStreamWrapper::getRoot()); + } + + /** + * assert that mkdir() creates the correct directory structure + * + * @test + * @group permissions + */ + public function mkdirNonRecursively() + { + $this->assertFalse(mkdir($this->barURL . '/another/more')); + $this->assertEquals(2, count($this->foo->getChildren())); + $this->assertTrue(mkdir($this->fooURL . '/another')); + $this->assertEquals(3, count($this->foo->getChildren())); + $this->assertEquals(0777, $this->foo->getChild('another')->getPermissions()); + } + + /** + * assert that mkdir() creates the correct directory structure + * + * @test + * @group permissions + */ + public function mkdirRecursively() + { + $this->assertTrue(mkdir($this->fooURL . '/another/more', 0777, true)); + $this->assertEquals(3, count($this->foo->getChildren())); + $another = $this->foo->getChild('another'); + $this->assertTrue($another->hasChild('more')); + $this->assertEquals(0777, $this->foo->getChild('another')->getPermissions()); + $this->assertEquals(0777, $this->foo->getChild('another')->getChild('more')->getPermissions()); + } + + /** + * @test + * @group issue_9 + * @since 0.9.0 + */ + public function mkdirWithDots() + { + $this->assertTrue(mkdir($this->fooURL . '/another/../more/.', 0777, true)); + $this->assertEquals(3, count($this->foo->getChildren())); + $this->assertTrue($this->foo->hasChild('more')); + } + + /** + * no root > new directory becomes root + * + * @test + * @group permissions + */ + public function mkdirWithoutRootCreatesNewRoot() + { + vfsStreamWrapper::register(); + $this->assertTrue(@mkdir(vfsStream::url('foo'))); + $this->assertEquals(vfsStreamContent::TYPE_DIR, vfsStreamWrapper::getRoot()->getType()); + $this->assertEquals('foo', vfsStreamWrapper::getRoot()->getName()); + $this->assertEquals(0777, vfsStreamWrapper::getRoot()->getPermissions()); + } + + /** + * trying to create a subdirectory of a file should not work + * + * @test + */ + public function mkdirOnFileReturnsFalse() + { + $this->assertFalse(mkdir($this->baz1URL . '/another/more', 0777, true)); + } + + /** + * assert that mkdir() creates the correct directory structure + * + * @test + * @group permissions + */ + public function mkdirNonRecursivelyDifferentPermissions() + { + $this->assertTrue(mkdir($this->fooURL . '/another', 0755)); + $this->assertEquals(0755, $this->foo->getChild('another')->getPermissions()); + } + + /** + * assert that mkdir() creates the correct directory structure + * + * @test + * @group permissions + */ + public function mkdirRecursivelyDifferentPermissions() + { + $this->assertTrue(mkdir($this->fooURL . '/another/more', 0755, true)); + $this->assertEquals(3, count($this->foo->getChildren())); + $another = $this->foo->getChild('another'); + $this->assertTrue($another->hasChild('more')); + $this->assertEquals(0755, $this->foo->getChild('another')->getPermissions()); + $this->assertEquals(0755, $this->foo->getChild('another')->getChild('more')->getPermissions()); + } + + /** + * assert that mkdir() creates the correct directory structure + * + * @test + * @group permissions + */ + public function mkdirRecursivelyUsesDefaultPermissions() + { + $this->foo->chmod(0700); + $this->assertTrue(mkdir($this->fooURL . '/another/more', 0777, true)); + $this->assertEquals(3, count($this->foo->getChildren())); + $another = $this->foo->getChild('another'); + $this->assertTrue($another->hasChild('more')); + $this->assertEquals(0777, $this->foo->getChild('another')->getPermissions()); + $this->assertEquals(0777, $this->foo->getChild('another')->getChild('more')->getPermissions()); + } + + /** + * no root > new directory becomes root + * + * @test + * @group permissions + */ + public function mkdirWithoutRootCreatesNewRootDifferentPermissions() + { + vfsStreamWrapper::register(); + $this->assertTrue(@mkdir(vfsStream::url('foo'), 0755)); + $this->assertEquals(vfsStreamContent::TYPE_DIR, vfsStreamWrapper::getRoot()->getType()); + $this->assertEquals('foo', vfsStreamWrapper::getRoot()->getName()); + $this->assertEquals(0755, vfsStreamWrapper::getRoot()->getPermissions()); + } + + /** + * no root > new directory becomes root + * + * @test + * @group permissions + */ + public function mkdirWithoutRootCreatesNewRootWithDefaultPermissions() + { + vfsStreamWrapper::register(); + $this->assertTrue(@mkdir(vfsStream::url('foo'))); + $this->assertEquals(vfsStreamContent::TYPE_DIR, vfsStreamWrapper::getRoot()->getType()); + $this->assertEquals('foo', vfsStreamWrapper::getRoot()->getName()); + $this->assertEquals(0777, vfsStreamWrapper::getRoot()->getPermissions()); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function mkdirDirCanNotCreateNewDirInNonWritingDirectory() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root')); + vfsStreamWrapper::getRoot()->addChild(new vfsStreamDirectory('restrictedFolder', 0000)); + $this->assertFalse(is_writable(vfsStream::url('root/restrictedFolder/'))); + $this->assertFalse(mkdir(vfsStream::url('root/restrictedFolder/newFolder'))); + $this->assertFalse(vfsStreamWrapper::getRoot()->hasChild('restrictedFolder/newFolder')); + } + + /** + * @test + * @group issue_28 + */ + public function mkDirShouldNotOverwriteExistingDirectories() + { + vfsStream::setup('root'); + $dir = vfsStream::url('root/dir'); + $this->assertTrue(mkdir($dir)); + $this->assertFalse(@mkdir($dir)); + } + + /** + * @test + * @group issue_28 + * @expectedException PHPUnit_Framework_Error + * @expectedExceptionMessage mkdir(): Path vfs://root/dir exists + */ + public function mkDirShouldNotOverwriteExistingDirectoriesAndTriggerE_USER_WARNING() + { + vfsStream::setup('root'); + $dir = vfsStream::url('root/dir'); + $this->assertTrue(mkdir($dir)); + $this->assertFalse(mkdir($dir)); + } + + /** + * @test + * @group issue_28 + */ + public function mkDirShouldNotOverwriteExistingFiles() + { + $root = vfsStream::setup('root'); + vfsStream::newFile('test.txt')->at($root); + $this->assertFalse(@mkdir(vfsStream::url('root/test.txt'))); + } + + /** + * @test + * @group issue_28 + * @expectedException PHPUnit_Framework_Error + * @expectedExceptionMessage mkdir(): Path vfs://root/test.txt exists + */ + public function mkDirShouldNotOverwriteExistingFilesAndTriggerE_USER_WARNING() + { + $root = vfsStream::setup('root'); + vfsStream::newFile('test.txt')->at($root); + $this->assertFalse(mkdir(vfsStream::url('root/test.txt'))); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function canNotIterateOverNonReadableDirectory() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root', 0000)); + $this->assertFalse(@opendir(vfsStream::url('root'))); + $this->assertFalse(@dir(vfsStream::url('root'))); + } + + /** + * @test + */ + public function directoryIteration() + { + $dir = dir($this->fooURL); + $i = 0; + while (false !== ($entry = $dir->read())) { + $i++; + $this->assertTrue('bar' === $entry || 'baz2' === $entry); + } + + $this->assertEquals(2, $i, 'Directory foo contains two children, but got ' . $i . ' children while iterating over directory contents'); + $dir->rewind(); + $i = 0; + while (false !== ($entry = $dir->read())) { + $i++; + $this->assertTrue('bar' === $entry || 'baz2' === $entry); + } + + $this->assertEquals(2, $i, 'Directory foo contains two children, but got ' . $i . ' children while iterating over directory contents'); + $dir->close(); + } + + /** + * @test + */ + public function directoryIterationWithDot() + { + $dir = dir($this->fooURL . '/.'); + $i = 0; + while (false !== ($entry = $dir->read())) { + $i++; + $this->assertTrue('bar' === $entry || 'baz2' === $entry); + } + + $this->assertEquals(2, $i, 'Directory foo contains two children, but got ' . $i . ' children while iterating over directory contents'); + $dir->rewind(); + $i = 0; + while (false !== ($entry = $dir->read())) { + $i++; + $this->assertTrue('bar' === $entry || 'baz2' === $entry); + } + + $this->assertEquals(2, $i, 'Directory foo contains two children, but got ' . $i . ' children while iterating over directory contents'); + $dir->close(); + } + + /** + * assure that a directory iteration works as expected + * + * @test + * @group regression + * @group bug_2 + */ + public function directoryIterationWithOpenDir_Bug_2() + { + $handle = opendir($this->fooURL); + $i = 0; + while (false !== ($entry = readdir($handle))) { + $i++; + $this->assertTrue('bar' === $entry || 'baz2' === $entry); + } + + $this->assertEquals(2, $i, 'Directory foo contains two children, but got ' . $i . ' children while iterating over directory contents'); + + rewind($handle); + $i = 0; + while (false !== ($entry = readdir($handle))) { + $i++; + $this->assertTrue('bar' === $entry || 'baz2' === $entry); + } + + $this->assertEquals(2, $i, 'Directory foo contains two children, but got ' . $i . ' children while iterating over directory contents'); + closedir($handle); + } + + /** + * assure that a directory iteration works as expected + * + * @author Christoph Bloemer + * @test + * @group regression + * @group bug_4 + */ + public function directoryIteration_Bug_4() + { + $dir = $this->fooURL; + $list1 = array(); + if ($handle = opendir($dir)) { + while (false !== ($listItem = readdir($handle))) { + if ('.' != $listItem && '..' != $listItem) { + if (is_file($dir . '/' . $listItem) === true) { + $list1[] = 'File:[' . $listItem . ']'; + } elseif (is_dir($dir . '/' . $listItem) === true) { + $list1[] = 'Folder:[' . $listItem . ']'; + } + } + } + + closedir($handle); + } + + $list2 = array(); + if ($handle = opendir($dir)) { + while (false !== ($listItem = readdir($handle))) { + if ('.' != $listItem && '..' != $listItem) { + if (is_file($dir . '/' . $listItem) === true) { + $list2[] = 'File:[' . $listItem . ']'; + } elseif (is_dir($dir . '/' . $listItem) === true) { + $list2[] = 'Folder:[' . $listItem . ']'; + } + } + } + + closedir($handle); + } + + $this->assertEquals($list1, $list2); + $this->assertEquals(2, count($list1)); + $this->assertEquals(2, count($list2)); + } + + /** + * assure that a directory iteration works as expected + * + * @test + */ + public function directoryIterationShouldBeIndependent() + { + $list1 = array(); + $list2 = array(); + $handle1 = opendir($this->fooURL); + if (false !== ($listItem = readdir($handle1))) { + $list1[] = $listItem; + } + + $handle2 = opendir($this->fooURL); + if (false !== ($listItem = readdir($handle2))) { + $list2[] = $listItem; + } + + if (false !== ($listItem = readdir($handle1))) { + $list1[] = $listItem; + } + + if (false !== ($listItem = readdir($handle2))) { + $list2[] = $listItem; + } + + closedir($handle1); + closedir($handle2); + $this->assertEquals($list1, $list2); + $this->assertEquals(2, count($list1)); + $this->assertEquals(2, count($list2)); + } + + /** + * assert is_dir() returns correct result + * + * @test + */ + public function is_dir() + { + $this->assertTrue(is_dir($this->fooURL)); + $this->assertTrue(is_dir($this->fooURL . '/.')); + $this->assertTrue(is_dir($this->barURL)); + $this->assertTrue(is_dir($this->barURL . '/.')); + $this->assertFalse(is_dir($this->baz1URL)); + $this->assertFalse(is_dir($this->baz2URL)); + $this->assertFalse(is_dir($this->fooURL . '/another')); + $this->assertFalse(is_dir(vfsStream::url('another'))); + } + + /** + * can not unlink without root + * + * @test + */ + public function canNotUnlinkDirectoryWithoutRoot() + { + vfsStreamWrapper::register(); + $this->assertFalse(@rmdir(vfsStream::url('foo'))); + } + + /** + * rmdir() can not remove files + * + * @test + */ + public function rmdirCanNotRemoveFiles() + { + $this->assertFalse(rmdir($this->baz1URL)); + $this->assertFalse(rmdir($this->baz2URL)); + } + + /** + * rmdir() can not remove a non-existing directory + * + * @test + */ + public function rmdirCanNotRemoveNonExistingDirectory() + { + $this->assertFalse(rmdir($this->fooURL . '/another')); + } + + /** + * rmdir() can not remove non-empty directories + * + * @test + */ + public function rmdirCanNotRemoveNonEmptyDirectory() + { + $this->assertFalse(rmdir($this->fooURL)); + $this->assertFalse(rmdir($this->barURL)); + } + + /** + * @test + */ + public function rmdirCanRemoveEmptyDirectory() + { + vfsStream::newDirectory('empty')->at($this->foo); + $this->assertTrue($this->foo->hasChild('empty')); + $this->assertTrue(rmdir($this->fooURL . '/empty')); + $this->assertFalse($this->foo->hasChild('empty')); + } + + /** + * @test + */ + public function rmdirCanRemoveEmptyDirectoryWithDot() + { + vfsStream::newDirectory('empty')->at($this->foo); + $this->assertTrue($this->foo->hasChild('empty')); + $this->assertTrue(rmdir($this->fooURL . '/empty/.')); + $this->assertFalse($this->foo->hasChild('empty')); + } + + /** + * rmdir() can remove empty directories + * + * @test + */ + public function rmdirCanRemoveEmptyRoot() + { + $this->foo->removeChild('bar'); + $this->foo->removeChild('baz2'); + $this->assertTrue(rmdir($this->fooURL)); + $this->assertFalse(file_exists($this->fooURL)); // make sure statcache was cleared + $this->assertNull(vfsStreamWrapper::getRoot()); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function rmdirDirCanNotRemoveDirFromNonWritingDirectory() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root', 0000)); + vfsStreamWrapper::getRoot()->addChild(new vfsStreamDirectory('nonRemovableFolder')); + $this->assertFalse(is_writable(vfsStream::url('root'))); + $this->assertFalse(rmdir(vfsStream::url('root/nonRemovableFolder'))); + $this->assertTrue(vfsStreamWrapper::getRoot()->hasChild('nonRemovableFolder')); + } + + /** + * @test + * @group permissions + * @group bug_17 + */ + public function issue17() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root', 0770)); + vfsStreamWrapper::getRoot()->chgrp(vfsStream::GROUP_USER_1) + ->chown(vfsStream::OWNER_USER_1); + $this->assertFalse(mkdir(vfsStream::url('root/doesNotWork'))); + $this->assertFalse(vfsStreamWrapper::getRoot()->hasChild('doesNotWork')); + } + + /** + * @test + * @group bug_19 + */ + public function accessWithDoubleDotReturnsCorrectContent() + { + $this->assertEquals('baz2', + file_get_contents(vfsStream::url('foo/bar/../baz2')) + ); + } + + /** + * @test + * @since 0.11.0 + * @group issue_23 + */ + public function unlinkCanNotRemoveNonEmptyDirectory() + { + try { + $this->assertFalse(unlink($this->barURL)); + } catch (\PHPUnit_Framework_Error $fe) { + $this->assertEquals('unlink(vfs://foo/bar): Operation not permitted', $fe->getMessage()); + } + + $this->assertTrue($this->foo->hasChild('bar')); + $this->assertFileExists($this->barURL); + } + + /** + * @test + * @since 0.11.0 + * @group issue_23 + */ + public function unlinkCanNotRemoveEmptyDirectory() + { + vfsStream::newDirectory('empty')->at($this->foo); + try { + $this->assertTrue(unlink($this->fooURL . '/empty')); + } catch (\PHPUnit_Framework_Error $fe) { + $this->assertEquals('unlink(vfs://foo/empty): Operation not permitted', $fe->getMessage()); + } + + $this->assertTrue($this->foo->hasChild('empty')); + $this->assertFileExists($this->fooURL . '/empty'); + } + + /** + * @test + * @group issue_32 + */ + public function canCreateFolderOfSameNameAsParentFolder() + { + $root = vfsStream::setup('testFolder'); + mkdir(vfsStream::url('testFolder') . '/testFolder/subTestFolder', 0777, true); + $this->assertTrue(file_exists(vfsStream::url('testFolder/testFolder/subTestFolder/.'))); + } + + /** + * @test + * @group issue_32 + */ + public function canRetrieveFolderOfSameNameAsParentFolder() + { + $root = vfsStream::setup('testFolder'); + mkdir(vfsStream::url('testFolder') . '/testFolder/subTestFolder', 0777, true); + $this->assertTrue($root->hasChild('testFolder')); + $this->assertNotNull($root->getChild('testFolder')); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFileTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFileTestCase.php new file mode 100644 index 00000000000..4ca9a61ab5f --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFileTestCase.php @@ -0,0 +1,472 @@ +assertEquals('baz2', file_get_contents($this->baz2URL)); + $this->assertEquals('baz 1', file_get_contents($this->baz1URL)); + $this->assertFalse(@file_get_contents($this->barURL)); + $this->assertFalse(@file_get_contents($this->fooURL)); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function file_get_contentsNonReadableFile() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root')); + vfsStream::newFile('new.txt', 0000)->at(vfsStreamWrapper::getRoot())->withContent('content'); + $this->assertEquals('', @file_get_contents(vfsStream::url('root/new.txt'))); + } + + /** + * assert that file_put_contents() delivers correct file contents + * + * @test + */ + public function file_put_contentsExistingFile() + { + $this->assertEquals(14, file_put_contents($this->baz2URL, 'baz is not bar')); + $this->assertEquals('baz is not bar', $this->baz2->getContent()); + $this->assertEquals(6, file_put_contents($this->baz1URL, 'foobar')); + $this->assertEquals('foobar', $this->baz1->getContent()); + $this->assertFalse(@file_put_contents($this->barURL, 'This does not work.')); + $this->assertFalse(@file_put_contents($this->fooURL, 'This does not work, too.')); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function file_put_contentsExistingFileNonWritableDirectory() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root', 0000)); + vfsStream::newFile('new.txt')->at(vfsStreamWrapper::getRoot())->withContent('content'); + $this->assertEquals(15, @file_put_contents(vfsStream::url('root/new.txt'), 'This does work.')); + $this->assertEquals('This does work.', file_get_contents(vfsStream::url('root/new.txt'))); + + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function file_put_contentsExistingNonWritableFile() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root')); + vfsStream::newFile('new.txt', 0400)->at(vfsStreamWrapper::getRoot())->withContent('content'); + $this->assertFalse(@file_put_contents(vfsStream::url('root/new.txt'), 'This does not work.')); + $this->assertEquals('content', file_get_contents(vfsStream::url('root/new.txt'))); + } + + /** + * assert that file_put_contents() delivers correct file contents + * + * @test + */ + public function file_put_contentsNonExistingFile() + { + $this->assertEquals(14, file_put_contents($this->fooURL . '/baznot.bar', 'baz is not bar')); + $this->assertEquals(3, count($this->foo->getChildren())); + $this->assertEquals(14, file_put_contents($this->barURL . '/baznot.bar', 'baz is not bar')); + $this->assertEquals(2, count($this->bar->getChildren())); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function file_put_contentsNonExistingFileNonWritableDirectory() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root', 0000)); + $this->assertFalse(@file_put_contents(vfsStream::url('root/new.txt'), 'This does not work.')); + $this->assertFalse(file_exists(vfsStream::url('root/new.txt'))); + + } + + /** + * using a file pointer should work without any problems + * + * @test + */ + public function usingFilePointer() + { + $fp = fopen($this->baz1URL, 'r'); + $this->assertEquals(0, ftell($fp)); + $this->assertFalse(feof($fp)); + $this->assertEquals(0, fseek($fp, 2)); + $this->assertEquals(2, ftell($fp)); + $this->assertEquals(0, fseek($fp, 1, SEEK_CUR)); + $this->assertEquals(3, ftell($fp)); + $this->assertEquals(0, fseek($fp, 1, SEEK_END)); + $this->assertEquals(6, ftell($fp)); + $this->assertTrue(feof($fp)); + $this->assertEquals(0, fseek($fp, 2)); + $this->assertFalse(feof($fp)); + $this->assertEquals(2, ftell($fp)); + $this->assertEquals('z', fread($fp, 1)); + $this->assertEquals(3, ftell($fp)); + $this->assertEquals(' 1', fread($fp, 8092)); + $this->assertEquals(5, ftell($fp)); + $this->assertTrue(fclose($fp)); + } + + /** + * assert is_file() returns correct result + * + * @test + */ + public function is_file() + { + $this->assertFalse(is_file($this->fooURL)); + $this->assertFalse(is_file($this->barURL)); + $this->assertTrue(is_file($this->baz1URL)); + $this->assertTrue(is_file($this->baz2URL)); + $this->assertFalse(is_file($this->fooURL . '/another')); + $this->assertFalse(is_file(vfsStream::url('another'))); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function issue13CanNotOverwriteFiles() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + file_put_contents($vfsFile, 'd'); + $this->assertEquals('d', file_get_contents($vfsFile)); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function appendContentIfOpenedWithModeA() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + $fp = fopen($vfsFile, 'ab'); + fwrite($fp, 'd'); + fclose($fp); + $this->assertEquals('testd', file_get_contents($vfsFile)); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canOverwriteNonExistingFileWithModeX() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + $fp = fopen($vfsFile, 'xb'); + fwrite($fp, 'test'); + fclose($fp); + $this->assertEquals('test', file_get_contents($vfsFile)); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotOverwriteExistingFileWithModeX() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + $this->assertFalse(@fopen($vfsFile, 'xb')); + $this->assertEquals('test', file_get_contents($vfsFile)); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotOpenNonExistingFileReadonly() + { + $this->assertFalse(@fopen(vfsStream::url('foo/doesNotExist.txt'), 'rb')); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotOpenNonExistingFileReadAndWrite() + { + $this->assertFalse(@fopen(vfsStream::url('foo/doesNotExist.txt'), 'rb+')); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotOpenWithIllegalMode() + { + $this->assertFalse(@fopen($this->baz2URL, 'invalid')); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotWriteToReadOnlyFile() + { + $fp = fopen($this->baz2URL, 'rb'); + $this->assertEquals('baz2', fread($fp, 4096)); + $this->assertEquals(0, fwrite($fp, 'foo')); + fclose($fp); + $this->assertEquals('baz2', file_get_contents($this->baz2URL)); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotReadFromWriteOnlyFileWithModeW() + { + $fp = fopen($this->baz2URL, 'wb'); + $this->assertEquals('', fread($fp, 4096)); + $this->assertEquals(3, fwrite($fp, 'foo')); + fseek($fp, 0); + $this->assertEquals('', fread($fp, 4096)); + fclose($fp); + $this->assertEquals('foo', file_get_contents($this->baz2URL)); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotReadFromWriteOnlyFileWithModeA() + { + $fp = fopen($this->baz2URL, 'ab'); + $this->assertEquals('', fread($fp, 4096)); + $this->assertEquals(3, fwrite($fp, 'foo')); + fseek($fp, 0); + $this->assertEquals('', fread($fp, 4096)); + fclose($fp); + $this->assertEquals('baz2foo', file_get_contents($this->baz2URL)); + } + + /** + * @test + * @group issue7 + * @group issue13 + */ + public function canNotReadFromWriteOnlyFileWithModeX() + { + $vfsFile = vfsStream::url('foo/modeXtest.txt'); + $fp = fopen($vfsFile, 'xb'); + $this->assertEquals('', fread($fp, 4096)); + $this->assertEquals(3, fwrite($fp, 'foo')); + fseek($fp, 0); + $this->assertEquals('', fread($fp, 4096)); + fclose($fp); + $this->assertEquals('foo', file_get_contents($vfsFile)); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function canNotRemoveFileWithoutWritePermissions() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root')); + vfsStream::newFile('new.txt', 0000)->at(vfsStreamWrapper::getRoot()); + $this->assertFalse(unlink(vfsStream::url('root/new.txt'))); + $this->assertTrue(file_exists(vfsStream::url('root/new.txt'))); + } + + /** + * @test + * @group permissions + * @group bug_15 + */ + public function canNotRemoveFileFromDirectoryWithoutWritePermissions() + { + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(new vfsStreamDirectory('root', 0000)); + vfsStream::newFile('new.txt')->at(vfsStreamWrapper::getRoot()); + $this->assertFalse(unlink(vfsStream::url('root/new.txt'))); + $this->assertTrue(file_exists(vfsStream::url('root/new.txt'))); + } + + /** + * @test + * @group issue_30 + */ + public function truncatesFileWhenOpenedWithModeW() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + $fp = fopen($vfsFile, 'wb'); + $this->assertEquals('', file_get_contents($vfsFile)); + fclose($fp); + } + + /** + * @test + * @group issue_30 + */ + public function createsNonExistingFileWhenOpenedWithModeC() + { + $vfsFile = vfsStream::url('foo/tobecreated.txt'); + $fp = fopen($vfsFile, 'cb'); + fwrite($fp, 'some content'); + $this->assertTrue($this->foo->hasChild('tobecreated.txt')); + fclose($fp); + $this->assertEquals('some content', file_get_contents($vfsFile)); + } + + /** + * @test + * @group issue_30 + */ + public function createsNonExistingFileWhenOpenedWithModeCplus() + { + $vfsFile = vfsStream::url('foo/tobecreated.txt'); + $fp = fopen($vfsFile, 'cb+'); + fwrite($fp, 'some content'); + $this->assertTrue($this->foo->hasChild('tobecreated.txt')); + fclose($fp); + $this->assertEquals('some content', file_get_contents($vfsFile)); + } + + /** + * @test + * @group issue_30 + */ + public function doesNotTruncateFileWhenOpenedWithModeC() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + $fp = fopen($vfsFile, 'cb'); + $this->assertEquals('test', file_get_contents($vfsFile)); + fclose($fp); + } + + /** + * @test + * @group issue_30 + */ + public function setsPointerToStartWhenOpenedWithModeC() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + $fp = fopen($vfsFile, 'cb'); + $this->assertEquals(0, ftell($fp)); + fclose($fp); + } + + /** + * @test + * @group issue_30 + */ + public function doesNotTruncateFileWhenOpenedWithModeCplus() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + $fp = fopen($vfsFile, 'cb+'); + $this->assertEquals('test', file_get_contents($vfsFile)); + fclose($fp); + } + + /** + * @test + * @group issue_30 + */ + public function setsPointerToStartWhenOpenedWithModeCplus() + { + $vfsFile = vfsStream::url('foo/overwrite.txt'); + file_put_contents($vfsFile, 'test'); + $fp = fopen($vfsFile, 'cb+'); + $this->assertEquals(0, ftell($fp)); + fclose($fp); + } + + /** + * @test + */ + public function cannotOpenExistingNonwritableFileWithModeA() + { + $this->baz1->chmod(0400); + $this->assertFalse(@fopen($this->baz1URL, 'a')); + } + + /** + * @test + */ + public function cannotOpenExistingNonwritableFileWithModeW() + { + $this->baz1->chmod(0400); + $this->assertFalse(@fopen($this->baz1URL, 'w')); + } + + /** + * @test + */ + public function cannotOpenNonReadableFileWithModeR() + { + $this->baz1->chmod(0); + $this->assertFalse(@fopen($this->baz1URL, 'r')); + } + + /** + * @test + */ + public function cannotRenameToNonWritableDir() + { + $this->bar->chmod(0); + $this->assertFalse(@rename($this->baz2URL, vfsStream::url('foo/bar/baz3'))); + } + + /** + * @test + * @group issue_38 + */ + public function cannotReadFileFromNonReadableDir() + { + $this->markTestSkipped("Issue #38."); + $this->bar->chmod(0); + $this->assertFalse(@file_get_contents($this->baz1URL)); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFileTimesTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFileTimesTestCase.php new file mode 100644 index 00000000000..2d036c0a6cf --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFileTimesTestCase.php @@ -0,0 +1,315 @@ +lastModified(50) + ->lastAccessed(50) + ->lastAttributeModified(50); + $this->fooUrl = vfsStream::url('root/foo.txt'); + $this->barUrl = vfsStream::url('root/bar'); + $this->bazUrl = vfsStream::url('root/bar/baz.txt'); + } + + /** + * helper assertion for the tests + * + * @param string $url url to check + * @param vfsStreamContent $content content to compare + */ + protected function assertFileTimesEqualStreamTimes($url, vfsStreamContent $content) + { + $this->assertEquals(filemtime($url), $content->filemtime()); + $this->assertEquals(fileatime($url), $content->fileatime()); + $this->assertEquals(filectime($url), $content->filectime()); + } + + /** + * @test + * @group issue_7 + * @group issue_26 + */ + public function openFileChangesAttributeTimeOnly() + { + $file = vfsStream::newFile('foo.txt') + ->withContent('test') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + fclose(fopen($this->fooUrl, 'rb')); + $this->assertGreaterThan(time() - 2, fileatime($this->fooUrl)); + $this->assertLessThanOrEqual(time(), fileatime($this->fooUrl)); + $this->assertLessThanOrEqual(100, filemtime($this->fooUrl)); + $this->assertEquals(100, filectime($this->fooUrl)); + $this->assertFileTimesEqualStreamTimes($this->fooUrl, $file); + } + + /** + * @test + * @group issue_7 + * @group issue_26 + */ + public function fileGetContentsChangesAttributeTimeOnly() + { + $file = vfsStream::newFile('foo.txt') + ->withContent('test') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + file_get_contents($this->fooUrl); + $this->assertGreaterThan(time() - 2, fileatime($this->fooUrl)); + $this->assertLessThanOrEqual(time(), fileatime($this->fooUrl)); + $this->assertLessThanOrEqual(100, filemtime($this->fooUrl)); + $this->assertEquals(100, filectime($this->fooUrl)); + $this->assertFileTimesEqualStreamTimes($this->fooUrl, $file); + } + + /** + * @test + * @group issue_7 + * @group issue_26 + */ + public function openFileWithTruncateChangesAttributeAndModificationTime() + { + $file = vfsStream::newFile('foo.txt') + ->withContent('test') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + fclose(fopen($this->fooUrl, 'wb')); + $this->assertGreaterThan(time() - 2, filemtime($this->fooUrl)); + $this->assertGreaterThan(time() - 2, fileatime($this->fooUrl)); + $this->assertLessThanOrEqual(time(), filemtime($this->fooUrl)); + $this->assertLessThanOrEqual(time(), fileatime($this->fooUrl)); + $this->assertEquals(100, filectime($this->fooUrl)); + $this->assertFileTimesEqualStreamTimes($this->fooUrl, $file); + } + + /** + * @test + * @group issue_7 + */ + public function readFileChangesAccessTime() + { + $file = vfsStream::newFile('foo.txt') + ->withContent('test') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + $fp = fopen($this->fooUrl, 'rb'); + $openTime = time(); + sleep(3); + fread($fp, 1024); + fclose($fp); + $this->assertLessThanOrEqual($openTime, filemtime($this->fooUrl)); + $this->assertLessThanOrEqual($openTime + 3, fileatime($this->fooUrl)); + $this->assertEquals(100, filectime($this->fooUrl)); + $this->assertFileTimesEqualStreamTimes($this->fooUrl, $file); + } + + /** + * @test + * @group issue_7 + */ + public function writeFileChangesModificationTime() + { + $file = vfsStream::newFile('foo.txt') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + $fp = fopen($this->fooUrl, 'wb'); + $openTime = time(); + sleep(3); + fwrite($fp, 'test'); + fclose($fp); + $this->assertLessThanOrEqual($openTime + 3, filemtime($this->fooUrl)); + $this->assertLessThanOrEqual($openTime, fileatime($this->fooUrl)); + $this->assertEquals(100, filectime($this->fooUrl)); + $this->assertFileTimesEqualStreamTimes($this->fooUrl, $file); + + } + + /** + * @test + * @group issue_7 + */ + public function createNewFileSetsAllTimesToCurrentTime() + { + file_put_contents($this->fooUrl, 'test'); + $this->assertLessThanOrEqual(time(), filemtime($this->fooUrl)); + $this->assertEquals(fileatime($this->fooUrl), filectime($this->fooUrl)); + $this->assertEquals(fileatime($this->fooUrl), filemtime($this->fooUrl)); + $this->assertFileTimesEqualStreamTimes($this->fooUrl, vfsStreamWrapper::getRoot()->getChild('foo.txt')); + } + + /** + * @test + * @group issue_7 + */ + public function createNewFileChangesAttributeAndModificationTimeOfContainingDirectory() + { + $dir = vfsStream::newDirectory('bar') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + file_put_contents($this->bazUrl, 'test'); + $this->assertLessThanOrEqual(time(), filemtime($this->barUrl)); + $this->assertLessThanOrEqual(time(), filectime($this->barUrl)); + $this->assertEquals(100, fileatime($this->barUrl)); + $this->assertFileTimesEqualStreamTimes($this->barUrl, $dir); + } + + /** + * @test + * @group issue_7 + */ + public function addNewFileNameWithLinkFunctionChangesAttributeTimeOfOriginalFile() + { + $this->markTestSkipped('Links are currently not supported by vfsStream.'); + } + + /** + * @test + * @group issue_7 + */ + public function addNewFileNameWithLinkFunctionChangesAttributeAndModificationTimeOfDirectoryContainingLink() + { + $this->markTestSkipped('Links are currently not supported by vfsStream.'); + } + + /** + * @test + * @group issue_7 + */ + public function removeFileChangesAttributeAndModificationTimeOfContainingDirectory() + { + $dir = vfsStream::newDirectory('bar') + ->at(vfsStreamWrapper::getRoot()); + $file = vfsStream::newFile('baz.txt') + ->at($dir) + ->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + $dir->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + unlink($this->bazUrl); + $this->assertLessThanOrEqual(time(), filemtime($this->barUrl)); + $this->assertLessThanOrEqual(time(), filectime($this->barUrl)); + $this->assertEquals(100, fileatime($this->barUrl)); + $this->assertFileTimesEqualStreamTimes($this->barUrl, $dir); + } + + /** + * @test + * @group issue_7 + */ + public function renameFileChangesAttributeAndModificationTimeOfAffectedDirectories() + { + $target = vfsStream::newDirectory('target') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(200) + ->lastAccessed(200) + ->lastAttributeModified(200); + $source = vfsStream::newDirectory('bar') + ->at(vfsStreamWrapper::getRoot()); + $file = vfsStream::newFile('baz.txt') + ->at($source) + ->lastModified(300) + ->lastAccessed(300) + ->lastAttributeModified(300); + $source->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + rename($this->bazUrl, vfsStream::url('root/target/baz.txt')); + $this->assertLessThanOrEqual(time(), filemtime($this->barUrl)); + $this->assertLessThanOrEqual(time(), filectime($this->barUrl)); + $this->assertEquals(100, fileatime($this->barUrl)); + $this->assertFileTimesEqualStreamTimes($this->barUrl, $source); + $this->assertLessThanOrEqual(time(), filemtime(vfsStream::url('root/target'))); + $this->assertLessThanOrEqual(time(), filectime(vfsStream::url('root/target'))); + $this->assertEquals(200, fileatime(vfsStream::url('root/target'))); + $this->assertFileTimesEqualStreamTimes(vfsStream::url('root/target'), $target); + } + + /** + * @test + * @group issue_7 + */ + public function renameFileDoesNotChangeFileTimesOfFileItself() + { + $target = vfsStream::newDirectory('target') + ->at(vfsStreamWrapper::getRoot()) + ->lastModified(200) + ->lastAccessed(200) + ->lastAttributeModified(200); + $source = vfsStream::newDirectory('bar') + ->at(vfsStreamWrapper::getRoot()); + $file = vfsStream::newFile('baz.txt') + ->at($source) + ->lastModified(300) + ->lastAccessed(300) + ->lastAttributeModified(300); + $source->lastModified(100) + ->lastAccessed(100) + ->lastAttributeModified(100); + rename($this->bazUrl, vfsStream::url('root/target/baz.txt')); + $this->assertEquals(300, filemtime(vfsStream::url('root/target/baz.txt'))); + $this->assertEquals(300, filectime(vfsStream::url('root/target/baz.txt'))); + $this->assertEquals(300, fileatime(vfsStream::url('root/target/baz.txt'))); + $this->assertFileTimesEqualStreamTimes(vfsStream::url('root/target/baz.txt'), $file); + } + + /** + * @test + * @group issue_7 + */ + public function changeFileAttributesChangesAttributeTimeOfFileItself() + { + $this->markTestSkipped('Changing file attributes via stream wrapper for self-defined streams is not supported by PHP.'); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFlockTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFlockTestCase.php new file mode 100644 index 00000000000..8c07308d340 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperFlockTestCase.php @@ -0,0 +1,240 @@ +root = vfsStream::setup(); + } + + /** + * @test + */ + public function fileIsNotLockedByDefault() + { + $this->assertFalse(vfsStream::newFile('foo.txt')->isLocked()); + } + + /** + * @test + */ + public function streamIsNotLockedByDefault() + { + file_put_contents(vfsStream::url('root/foo.txt'), 'content'); + $this->assertFalse($this->root->getChild('foo.txt')->isLocked()); + } + + /** + * @test + */ + public function canAquireSharedLock() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertTrue(flock($fp, LOCK_SH)); + $this->assertTrue($file->isLocked()); + $this->assertTrue($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + fclose($fp); + + } + + /** + * @test + */ + public function canAquireSharedLockWithNonBlockingFlockCall() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertTrue(flock($fp, LOCK_SH | LOCK_NB)); + $this->assertTrue($file->isLocked()); + $this->assertTrue($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + fclose($fp); + + } + + /** + * @test + */ + public function canAquireEclusiveLock() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertTrue(flock($fp, LOCK_EX)); + $this->assertTrue($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertTrue($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @test + */ + public function canAquireEclusiveLockWithNonBlockingFlockCall() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertTrue(flock($fp, LOCK_EX | LOCK_NB)); + $this->assertTrue($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertTrue($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @test + */ + public function canRemoveLock() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $file->lock(LOCK_EX); + $this->assertTrue(flock($fp, LOCK_UN)); + $this->assertFalse($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @test + */ + public function canRemoveLockWithNonBlockingFlockCall() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $file->lock(LOCK_EX); + $this->assertTrue(flock($fp, LOCK_UN | LOCK_NB)); + $this->assertFalse($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @see https://github.com/mikey179/vfsStream/issues/31 + * @test + * @group issue_31 + */ + public function canNotAquireExclusiveLockIfAlreadyExclusivelyLocked() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $file->lock(LOCK_EX); + $this->assertFalse(flock($fp, LOCK_EX + LOCK_NB)); + $this->assertTrue($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertTrue($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @see https://github.com/mikey179/vfsStream/issues/31 + * @test + * @group issue_31 + */ + public function canNotAquireExclusiveLockIfAlreadySharedLocked() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $file->lock(LOCK_SH); + $this->assertFalse(flock($fp, LOCK_EX)); + $this->assertTrue($file->isLocked()); + $this->assertTrue($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @see https://github.com/mikey179/vfsStream/issues/31 + * @test + * @group issue_31 + */ + public function canNotAquireSharedLockIfAlreadyExclusivelyLocked() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $file->lock(LOCK_EX); + $this->assertFalse(flock($fp, LOCK_SH + LOCK_NB)); + $this->assertTrue($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertTrue($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @see https://github.com/mikey179/vfsStream/issues/31 + * @test + * @group issue_31 + */ + public function canAquireSharedLockIfAlreadySharedLocked() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $file->lock(LOCK_SH); + $this->assertTrue(flock($fp, LOCK_SH)); + $this->assertTrue($file->isLocked()); + $this->assertTrue($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + fclose($fp); + } + + /** + * @see https://github.com/mikey179/vfsStream/issues/31 + * @test + * @group issue_31 + */ + public function removesExclusiveLockOnStreamClose() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $file->lock(LOCK_EX); + fclose(fopen(vfsStream::url('root/foo.txt'), 'rb')); + $this->assertFalse($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + } + + /** + * @see https://github.com/mikey179/vfsStream/issues/31 + * @test + * @group issue_31 + */ + public function removesSharedLockOnStreamClose() + { + $file = vfsStream::newFile('foo.txt')->at($this->root); + $file->lock(LOCK_SH); + fclose(fopen(vfsStream::url('root/foo.txt'), 'rb')); + $this->assertFalse($file->isLocked()); + $this->assertFalse($file->hasSharedLock()); + $this->assertFalse($file->hasExclusiveLock()); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperQuotaTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperQuotaTestCase.php new file mode 100644 index 00000000000..84c454619e0 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperQuotaTestCase.php @@ -0,0 +1,204 @@ +root = vfsStream::setup(); + vfsStream::setQuota(10); + } + + /** + * @test + */ + public function writeLessThanQuotaWritesEverything() + { + $this->assertEquals(9, file_put_contents(vfsStream::url('root/file.txt'), '123456789')); + $this->assertEquals('123456789', $this->root->getChild('file.txt')->getContent()); + } + + /** + * @test + */ + public function writeUpToQotaWritesEverything() + { + $this->assertEquals(10, file_put_contents(vfsStream::url('root/file.txt'), '1234567890')); + $this->assertEquals('1234567890', $this->root->getChild('file.txt')->getContent()); + } + + /** + * @test + */ + public function writeMoreThanQotaWritesOnlyUpToQuota() + { + try { + file_put_contents(vfsStream::url('root/file.txt'), '12345678901'); + } catch (\PHPUnit_Framework_Error $e) { + $this->assertEquals('file_put_contents(): Only 10 of 11 bytes written, possibly out of free disk space', + $e->getMessage() + ); + } + + $this->assertEquals('1234567890', $this->root->getChild('file.txt')->getContent()); + } + + /** + * @test + */ + public function considersAllFilesForQuota() + { + vfsStream::newFile('foo.txt') + ->withContent('foo') + ->at(vfsStream::newDirectory('bar') + ->at($this->root) + ); + try { + file_put_contents(vfsStream::url('root/file.txt'), '12345678901'); + } catch (\PHPUnit_Framework_Error $e) { + $this->assertEquals('file_put_contents(): Only 7 of 11 bytes written, possibly out of free disk space', + $e->getMessage() + ); + } + + $this->assertEquals('1234567', $this->root->getChild('file.txt')->getContent()); + } + + /** + * @test + * @group issue_33 + */ + public function truncateToLessThanQuotaWritesEverything() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $fp = fopen(vfsStream::url('root/file.txt'), 'w+'); + $this->assertTrue(ftruncate($fp, 9)); + fclose($fp); + $this->assertEquals(9, + $this->root->getChild('file.txt')->size() + ); + $this->assertEquals("\0\0\0\0\0\0\0\0\0", + $this->root->getChild('file.txt')->getContent() + ); + } + + /** + * @test + * @group issue_33 + */ + public function truncateUpToQotaWritesEverything() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $fp = fopen(vfsStream::url('root/file.txt'), 'w+'); + $this->assertTrue(ftruncate($fp, 10)); + fclose($fp); + $this->assertEquals(10, + $this->root->getChild('file.txt')->size() + ); + $this->assertEquals("\0\0\0\0\0\0\0\0\0\0", + $this->root->getChild('file.txt')->getContent() + ); + } + + /** + * @test + * @group issue_33 + */ + public function truncateToMoreThanQotaWritesOnlyUpToQuota() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $fp = fopen(vfsStream::url('root/file.txt'), 'w+'); + $this->assertTrue(ftruncate($fp, 11)); + fclose($fp); + $this->assertEquals(10, + $this->root->getChild('file.txt')->size() + ); + $this->assertEquals("\0\0\0\0\0\0\0\0\0\0", + $this->root->getChild('file.txt')->getContent() + ); + } + + /** + * @test + * @group issue_33 + */ + public function truncateConsidersAllFilesForQuota() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + vfsStream::newFile('bar.txt') + ->withContent('bar') + ->at(vfsStream::newDirectory('bar') + ->at($this->root) + ); + $fp = fopen(vfsStream::url('root/file.txt'), 'w+'); + $this->assertTrue(ftruncate($fp, 11)); + fclose($fp); + $this->assertEquals(7, + $this->root->getChild('file.txt')->size() + ); + $this->assertEquals("\0\0\0\0\0\0\0", + $this->root->getChild('file.txt')->getContent() + ); + } + + /** + * @test + * @group issue_33 + */ + public function canNotTruncateToGreaterLengthWhenDiscQuotaReached() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + vfsStream::newFile('bar.txt') + ->withContent('1234567890') + ->at(vfsStream::newDirectory('bar') + ->at($this->root) + ); + $fp = fopen(vfsStream::url('root/file.txt'), 'w+'); + $this->assertFalse(ftruncate($fp, 11)); + fclose($fp); + $this->assertEquals(0, + $this->root->getChild('file.txt')->size() + ); + $this->assertEquals('', + $this->root->getChild('file.txt')->getContent() + ); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperSetOptionTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperSetOptionTestCase.php new file mode 100644 index 00000000000..aa86bd343a1 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperSetOptionTestCase.php @@ -0,0 +1,76 @@ +root = vfsStream::setup(); + vfsStream::newFile('foo.txt')->at($this->root); + } + + /** + * @test + */ + public function setBlockingDoesNotWork() + { + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertFalse(stream_set_blocking($fp, 1)); + fclose($fp); + } + + /** + * @test + */ + public function removeBlockingDoesNotWork() + { + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertFalse(stream_set_blocking($fp, 0)); + fclose($fp); + } + + /** + * @test + */ + public function setTimeoutDoesNotWork() + { + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertFalse(stream_set_timeout($fp, 1)); + fclose($fp); + } + + /** + * @test + */ + public function setWriteBufferDoesNotWork() + { + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $this->assertEquals(-1, stream_set_write_buffer($fp, 512)); + fclose($fp); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperStreamSelectTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperStreamSelectTestCase.php new file mode 100644 index 00000000000..c2aec99713a --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperStreamSelectTestCase.php @@ -0,0 +1,35 @@ +at($root)->withContent('testContent'); + + $fp = fopen(vfsStream::url('root/foo.txt'), 'rb'); + $readarray = array($fp); + $writearray = array(); + $exceptarray = array(); + stream_select($readarray, $writearray, $exceptarray, 1); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperTestCase.php new file mode 100644 index 00000000000..2f5bb4364f3 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperTestCase.php @@ -0,0 +1,711 @@ +assertSame($this->foo, vfsStreamWrapper::getRoot()); + vfsStreamWrapper::register(); + $this->assertNull(vfsStreamWrapper::getRoot()); + } + + /** + * @test + * @since 0.11.0 + */ + public function setRootReturnsRoot() + { + vfsStreamWrapper::register(); + $root = vfsStream::newDirectory('root'); + $this->assertSame($root, vfsStreamWrapper::setRoot($root)); + } + + /** + * assure that filesize is returned correct + * + * @test + */ + public function filesize() + { + $this->assertEquals(0, filesize($this->fooURL)); + $this->assertEquals(0, filesize($this->fooURL . '/.')); + $this->assertEquals(0, filesize($this->barURL)); + $this->assertEquals(0, filesize($this->barURL . '/.')); + $this->assertEquals(4, filesize($this->baz2URL)); + $this->assertEquals(5, filesize($this->baz1URL)); + } + + /** + * assert that file_exists() delivers correct result + * + * @test + */ + public function file_exists() + { + $this->assertTrue(file_exists($this->fooURL)); + $this->assertTrue(file_exists($this->fooURL . '/.')); + $this->assertTrue(file_exists($this->barURL)); + $this->assertTrue(file_exists($this->barURL . '/.')); + $this->assertTrue(file_exists($this->baz1URL)); + $this->assertTrue(file_exists($this->baz2URL)); + $this->assertFalse(file_exists($this->fooURL . '/another')); + $this->assertFalse(file_exists(vfsStream::url('another'))); + } + + /** + * assert that filemtime() delivers correct result + * + * @test + */ + public function filemtime() + { + $this->assertEquals(100, filemtime($this->fooURL)); + $this->assertEquals(100, filemtime($this->fooURL . '/.')); + $this->assertEquals(200, filemtime($this->barURL)); + $this->assertEquals(200, filemtime($this->barURL . '/.')); + $this->assertEquals(300, filemtime($this->baz1URL)); + $this->assertEquals(400, filemtime($this->baz2URL)); + } + + /** + * @test + * @group issue_23 + */ + public function unlinkRemovesFilesOnly() + { + $this->assertTrue(unlink($this->baz2URL)); + $this->assertFalse(file_exists($this->baz2URL)); // make sure statcache was cleared + $this->assertEquals(array($this->bar), $this->foo->getChildren()); + $this->assertFalse(unlink($this->fooURL . '/another')); + $this->assertFalse(unlink(vfsStream::url('another'))); + $this->assertEquals(array($this->bar), $this->foo->getChildren()); + } + + /** + * assert dirname() returns correct directory name + * + * @test + */ + public function dirname() + { + $this->assertEquals($this->fooURL, dirname($this->barURL)); + $this->assertEquals($this->barURL, dirname($this->baz1URL)); + # returns "vfs:" instead of "." + # however this seems not to be fixable because dirname() does not + # call the stream wrapper + #$this->assertEquals(dirname(vfsStream::url('doesNotExist')), '.'); + } + + /** + * assert basename() returns correct file name + * + * @test + */ + public function basename() + { + $this->assertEquals('bar', basename($this->barURL)); + $this->assertEquals('baz1', basename($this->baz1URL)); + $this->assertEquals('doesNotExist', basename(vfsStream::url('doesNotExist'))); + } + + /** + * assert is_readable() works correct + * + * @test + */ + public function is_readable() + { + $this->assertTrue(is_readable($this->fooURL)); + $this->assertTrue(is_readable($this->fooURL . '/.')); + $this->assertTrue(is_readable($this->barURL)); + $this->assertTrue(is_readable($this->barURL . '/.')); + $this->assertTrue(is_readable($this->baz1URL)); + $this->assertTrue(is_readable($this->baz2URL)); + $this->assertFalse(is_readable($this->fooURL . '/another')); + $this->assertFalse(is_readable(vfsStream::url('another'))); + + $this->foo->chmod(0222); + $this->assertFalse(is_readable($this->fooURL)); + + $this->baz1->chmod(0222); + $this->assertFalse(is_readable($this->baz1URL)); + } + + /** + * assert is_writable() works correct + * + * @test + */ + public function is_writable() + { + $this->assertTrue(is_writable($this->fooURL)); + $this->assertTrue(is_writable($this->fooURL . '/.')); + $this->assertTrue(is_writable($this->barURL)); + $this->assertTrue(is_writable($this->barURL . '/.')); + $this->assertTrue(is_writable($this->baz1URL)); + $this->assertTrue(is_writable($this->baz2URL)); + $this->assertFalse(is_writable($this->fooURL . '/another')); + $this->assertFalse(is_writable(vfsStream::url('another'))); + + $this->foo->chmod(0444); + $this->assertFalse(is_writable($this->fooURL)); + + $this->baz1->chmod(0444); + $this->assertFalse(is_writable($this->baz1URL)); + } + + /** + * assert is_executable() works correct + * + * @test + */ + public function is_executable() + { + $this->assertFalse(is_executable($this->baz1URL)); + $this->baz1->chmod(0766); + $this->assertTrue(is_executable($this->baz1URL)); + $this->assertFalse(is_executable($this->baz2URL)); + } + + /** + * assert is_executable() works correct + * + * @test + */ + public function directoriesAndNonExistingFilesAreNeverExecutable() + { + $this->assertFalse(is_executable($this->fooURL)); + $this->assertFalse(is_executable($this->fooURL . '/.')); + $this->assertFalse(is_executable($this->barURL)); + $this->assertFalse(is_executable($this->barURL . '/.')); + $this->assertFalse(is_executable($this->fooURL . '/another')); + $this->assertFalse(is_executable(vfsStream::url('another'))); + } + + /** + * file permissions + * + * @test + * @group permissions + */ + public function chmod() + { + $this->assertEquals(40777, decoct(fileperms($this->fooURL))); + $this->assertEquals(40777, decoct(fileperms($this->fooURL . '/.'))); + $this->assertEquals(40777, decoct(fileperms($this->barURL))); + $this->assertEquals(40777, decoct(fileperms($this->barURL . '/.'))); + $this->assertEquals(100666, decoct(fileperms($this->baz1URL))); + $this->assertEquals(100666, decoct(fileperms($this->baz2URL))); + + $this->foo->chmod(0755); + $this->bar->chmod(0700); + $this->baz1->chmod(0644); + $this->baz2->chmod(0600); + $this->assertEquals(40755, decoct(fileperms($this->fooURL))); + $this->assertEquals(40755, decoct(fileperms($this->fooURL . '/.'))); + $this->assertEquals(40700, decoct(fileperms($this->barURL))); + $this->assertEquals(40700, decoct(fileperms($this->barURL . '/.'))); + $this->assertEquals(100644, decoct(fileperms($this->baz1URL))); + $this->assertEquals(100600, decoct(fileperms($this->baz2URL))); + } + + /** + * @test + * @group issue_11 + * @group permissions + */ + public function chmodModifiesPermissions() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->assertFalse(@chmod($this->fooURL, 0755)); + $this->assertFalse(@chmod($this->barURL, 0711)); + $this->assertFalse(@chmod($this->baz1URL, 0644)); + $this->assertFalse(@chmod($this->baz2URL, 0664)); + $this->assertEquals(40777, decoct(fileperms($this->fooURL))); + $this->assertEquals(40777, decoct(fileperms($this->barURL))); + $this->assertEquals(100666, decoct(fileperms($this->baz1URL))); + $this->assertEquals(100666, decoct(fileperms($this->baz2URL))); + } else { + $this->assertTrue(chmod($this->fooURL, 0755)); + $this->assertTrue(chmod($this->barURL, 0711)); + $this->assertTrue(chmod($this->baz1URL, 0644)); + $this->assertTrue(chmod($this->baz2URL, 0664)); + $this->assertEquals(40755, decoct(fileperms($this->fooURL))); + $this->assertEquals(40711, decoct(fileperms($this->barURL))); + $this->assertEquals(100644, decoct(fileperms($this->baz1URL))); + $this->assertEquals(100664, decoct(fileperms($this->baz2URL))); + } + } + + /** + * @test + * @group permissions + */ + public function fileownerIsCurrentUserByDefault() + { + $this->assertEquals(vfsStream::getCurrentUser(), fileowner($this->fooURL)); + $this->assertEquals(vfsStream::getCurrentUser(), fileowner($this->fooURL . '/.')); + $this->assertEquals(vfsStream::getCurrentUser(), fileowner($this->barURL)); + $this->assertEquals(vfsStream::getCurrentUser(), fileowner($this->barURL . '/.')); + $this->assertEquals(vfsStream::getCurrentUser(), fileowner($this->baz1URL)); + $this->assertEquals(vfsStream::getCurrentUser(), fileowner($this->baz2URL)); + } + + /** + * @test + * @group issue_11 + * @group permissions + */ + public function chownChangesUser() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->foo->chown(vfsStream::OWNER_USER_1); + $this->bar->chown(vfsStream::OWNER_USER_1); + $this->baz1->chown(vfsStream::OWNER_USER_2); + $this->baz2->chown(vfsStream::OWNER_USER_2); + } else { + chown($this->fooURL, vfsStream::OWNER_USER_1); + chown($this->barURL, vfsStream::OWNER_USER_1); + chown($this->baz1URL, vfsStream::OWNER_USER_2); + chown($this->baz2URL, vfsStream::OWNER_USER_2); + } + + $this->assertEquals(vfsStream::OWNER_USER_1, fileowner($this->fooURL)); + $this->assertEquals(vfsStream::OWNER_USER_1, fileowner($this->fooURL . '/.')); + $this->assertEquals(vfsStream::OWNER_USER_1, fileowner($this->barURL)); + $this->assertEquals(vfsStream::OWNER_USER_1, fileowner($this->barURL . '/.')); + $this->assertEquals(vfsStream::OWNER_USER_2, fileowner($this->baz1URL)); + $this->assertEquals(vfsStream::OWNER_USER_2, fileowner($this->baz2URL)); + } + + /** + * @test + * @group issue_11 + * @group permissions + */ + public function chownDoesNotWorkOnVfsStreamUrls() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->assertFalse(@chown($this->fooURL, vfsStream::OWNER_USER_2)); + $this->assertEquals(vfsStream::getCurrentUser(), fileowner($this->fooURL)); + } + } + + /** + * @test + * @group issue_11 + * @group permissions + */ + public function groupIsCurrentGroupByDefault() + { + $this->assertEquals(vfsStream::getCurrentGroup(), filegroup($this->fooURL)); + $this->assertEquals(vfsStream::getCurrentGroup(), filegroup($this->fooURL . '/.')); + $this->assertEquals(vfsStream::getCurrentGroup(), filegroup($this->barURL)); + $this->assertEquals(vfsStream::getCurrentGroup(), filegroup($this->barURL . '/.')); + $this->assertEquals(vfsStream::getCurrentGroup(), filegroup($this->baz1URL)); + $this->assertEquals(vfsStream::getCurrentGroup(), filegroup($this->baz2URL)); + } + + /** + * @test + * @group issue_11 + * @group permissions + */ + public function chgrp() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->foo->chgrp(vfsStream::GROUP_USER_1); + $this->bar->chgrp(vfsStream::GROUP_USER_1); + $this->baz1->chgrp(vfsStream::GROUP_USER_2); + $this->baz2->chgrp(vfsStream::GROUP_USER_2); + } else { + chgrp($this->fooURL, vfsStream::GROUP_USER_1); + chgrp($this->barURL, vfsStream::GROUP_USER_1); + chgrp($this->baz1URL, vfsStream::GROUP_USER_2); + chgrp($this->baz2URL, vfsStream::GROUP_USER_2); + } + + $this->assertEquals(vfsStream::GROUP_USER_1, filegroup($this->fooURL)); + $this->assertEquals(vfsStream::GROUP_USER_1, filegroup($this->fooURL . '/.')); + $this->assertEquals(vfsStream::GROUP_USER_1, filegroup($this->barURL)); + $this->assertEquals(vfsStream::GROUP_USER_1, filegroup($this->barURL . '/.')); + $this->assertEquals(vfsStream::GROUP_USER_2, filegroup($this->baz1URL)); + $this->assertEquals(vfsStream::GROUP_USER_2, filegroup($this->baz2URL)); + } + + /** + * @test + * @group issue_11 + * @group permissions + */ + public function chgrpDoesNotWorkOnVfsStreamUrls() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->assertFalse(@chgrp($this->fooURL, vfsStream::GROUP_USER_2)); + $this->assertEquals(vfsStream::getCurrentGroup(), filegroup($this->fooURL)); + } + } + + /** + * @test + * @author Benoit Aubuchon + */ + public function renameDirectory() + { + // move foo/bar to foo/baz3 + $baz3URL = vfsStream::url('foo/baz3'); + $this->assertTrue(rename($this->barURL, $baz3URL)); + $this->assertFileExists($baz3URL); + $this->assertFileNotExists($this->barURL); + } + + /** + * @test + */ + public function renameDirectoryWithDots() + { + // move foo/bar to foo/baz3 + $baz3URL = vfsStream::url('foo/baz3'); + $this->assertTrue(rename($this->barURL . '/.', $baz3URL)); + $this->assertFileExists($baz3URL); + $this->assertFileNotExists($this->barURL); + } + + /** + * @test + * @group issue_9 + * @since 0.9.0 + */ + public function renameDirectoryWithDotsInTarget() + { + // move foo/bar to foo/baz3 + $baz3URL = vfsStream::url('foo/../foo/baz3/.'); + $this->assertTrue(rename($this->barURL . '/.', $baz3URL)); + $this->assertFileExists($baz3URL); + $this->assertFileNotExists($this->barURL); + } + + /** + * @test + * @author Benoit Aubuchon + */ + public function renameDirectoryOverwritingExistingFile() + { + // move foo/bar to foo/baz2 + $this->assertTrue(rename($this->barURL, $this->baz2URL)); + $this->assertFileExists(vfsStream::url('foo/baz2/baz1')); + $this->assertFileNotExists($this->barURL); + } + + /** + * @test + * @expectedException PHPUnit_Framework_Error + */ + public function renameFileIntoFile() + { + // foo/baz2 is a file, so it can not be turned into a directory + $baz3URL = vfsStream::url('foo/baz2/baz3'); + $this->assertTrue(rename($this->baz1URL, $baz3URL)); + $this->assertFileExists($baz3URL); + $this->assertFileNotExists($this->baz1URL); + } + + /** + * @test + * @author Benoit Aubuchon + */ + public function renameFileToDirectory() + { + // move foo/bar/baz1 to foo/baz3 + $baz3URL = vfsStream::url('foo/baz3'); + $this->assertTrue(rename($this->baz1URL, $baz3URL)); + $this->assertFileExists($this->barURL); + $this->assertFileExists($baz3URL); + $this->assertFileNotExists($this->baz1URL); + } + + /** + * assert that trying to rename from a non existing file trigger a warning + * + * @expectedException PHPUnit_Framework_Error + * @test + */ + public function renameOnSourceFileNotFound() + { + rename(vfsStream::url('notfound'), $this->baz1URL); + } + /** + * assert that trying to rename to a directory that is not found trigger a warning + + * @expectedException PHPUnit_Framework_Error + * @test + */ + public function renameOnDestinationDirectoryFileNotFound() + { + rename($this->baz1URL, vfsStream::url('foo/notfound/file2')); + } + /** + * stat() and fstat() should return the same result + * + * @test + */ + public function statAndFstatReturnSameResult() + { + $fp = fopen($this->baz2URL, 'r'); + $this->assertEquals(stat($this->baz2URL), + fstat($fp) + ); + fclose($fp); + } + + /** + * stat() returns full data + * + * @test + */ + public function statReturnsFullDataForFiles() + { + $this->assertEquals(array(0 => 0, + 1 => 0, + 2 => 0100666, + 3 => 0, + 4 => vfsStream::getCurrentUser(), + 5 => vfsStream::getCurrentGroup(), + 6 => 0, + 7 => 4, + 8 => 400, + 9 => 400, + 10 => 400, + 11 => -1, + 12 => -1, + 'dev' => 0, + 'ino' => 0, + 'mode' => 0100666, + 'nlink' => 0, + 'uid' => vfsStream::getCurrentUser(), + 'gid' => vfsStream::getCurrentGroup(), + 'rdev' => 0, + 'size' => 4, + 'atime' => 400, + 'mtime' => 400, + 'ctime' => 400, + 'blksize' => -1, + 'blocks' => -1 + ), + stat($this->baz2URL) + ); + } + + /** + * @test + */ + public function statReturnsFullDataForDirectories() + { + $this->assertEquals(array(0 => 0, + 1 => 0, + 2 => 0040777, + 3 => 0, + 4 => vfsStream::getCurrentUser(), + 5 => vfsStream::getCurrentGroup(), + 6 => 0, + 7 => 0, + 8 => 100, + 9 => 100, + 10 => 100, + 11 => -1, + 12 => -1, + 'dev' => 0, + 'ino' => 0, + 'mode' => 0040777, + 'nlink' => 0, + 'uid' => vfsStream::getCurrentUser(), + 'gid' => vfsStream::getCurrentGroup(), + 'rdev' => 0, + 'size' => 0, + 'atime' => 100, + 'mtime' => 100, + 'ctime' => 100, + 'blksize' => -1, + 'blocks' => -1 + ), + stat($this->fooURL) + ); + } + + /** + * @test + */ + public function statReturnsFullDataForDirectoriesWithDot() + { + $this->assertEquals(array(0 => 0, + 1 => 0, + 2 => 0040777, + 3 => 0, + 4 => vfsStream::getCurrentUser(), + 5 => vfsStream::getCurrentGroup(), + 6 => 0, + 7 => 0, + 8 => 100, + 9 => 100, + 10 => 100, + 11 => -1, + 12 => -1, + 'dev' => 0, + 'ino' => 0, + 'mode' => 0040777, + 'nlink' => 0, + 'uid' => vfsStream::getCurrentUser(), + 'gid' => vfsStream::getCurrentGroup(), + 'rdev' => 0, + 'size' => 0, + 'atime' => 100, + 'mtime' => 100, + 'ctime' => 100, + 'blksize' => -1, + 'blocks' => -1 + ), + stat($this->fooURL . '/.') + ); + } + + /** + * @test + * @expectedException PHPUnit_Framework_Error + */ + public function openFileWithoutDirectory() + { + vfsStreamWrapper::register(); + $this->assertFalse(file_get_contents(vfsStream::url('file.txt'))); + } + + /** + * @test + * @group issue_33 + * @since 1.1.0 + */ + public function truncateRemovesSuperflouosContent() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $handle = fopen($this->baz1URL, "r+"); + $this->assertTrue(ftruncate($handle, 0)); + $this->assertEquals(0, filesize($this->baz1URL)); + $this->assertEquals('', file_get_contents($this->baz1URL)); + fclose($handle); + } + + /** + * @test + * @group issue_33 + * @since 1.1.0 + */ + public function truncateToGreaterSizeAddsZeroBytes() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $handle = fopen($this->baz1URL, "r+"); + $this->assertTrue(ftruncate($handle, 25)); + $this->assertEquals(25, filesize($this->baz1URL)); + $this->assertEquals("baz 1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", + file_get_contents($this->baz1URL)); + fclose($handle); + } + + /** + * @test + * @group issue_11 + */ + public function touchCreatesNonExistingFile() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $this->assertTrue(touch($this->fooURL . '/new.txt')); + $this->assertTrue($this->foo->hasChild('new.txt')); + } + + /** + * @test + * @group issue_11 + */ + public function touchChangesAccessAndModificationTimeForFile() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $this->assertTrue(touch($this->baz1URL, 303, 313)); + $this->assertEquals(303, $this->baz1->filemtime()); + $this->assertEquals(313, $this->baz1->fileatime()); + } + + /** + * @test + * @group issue_11 + */ + public function touchDoesNotChangeTimesWhenNoTimesGiven() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $this->assertTrue(touch($this->baz1URL)); + $this->assertEquals(300, $this->baz1->filemtime()); + $this->assertEquals(300, $this->baz1->fileatime()); + } + + /** + * @test + * @group issue_11 + */ + public function touchWithModifiedTimeChangesAccessAndModifiedTime() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $this->assertTrue(touch($this->baz1URL, 303)); + $this->assertEquals(303, $this->baz1->filemtime()); + $this->assertEquals(303, $this->baz1->fileatime()); + } + + /** + * @test + * @group issue_11 + */ + public function touchChangesAccessAndModificationTimeForDirectory() + { + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $this->assertTrue(touch($this->fooURL, 303, 313)); + $this->assertEquals(303, $this->foo->filemtime()); + $this->assertEquals(313, $this->foo->fileatime()); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperWithoutRootTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperWithoutRootTestCase.php new file mode 100644 index 00000000000..6b030664391 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamWrapperWithoutRootTestCase.php @@ -0,0 +1,64 @@ + no directory to open + * + * @test + */ + public function canNotOpenDirectory() + { + $this->assertFalse(@dir(vfsStream::url('foo'))); + } + + /** + * can not unlink without root + * + * @test + */ + public function canNotUnlink() + { + $this->assertFalse(@unlink(vfsStream::url('foo'))); + } + + /** + * can not open a file without root + * + * @test + */ + public function canNotOpen() + { + $this->assertFalse(@fopen(vfsStream::url('foo'))); + } + + /** + * can not rename a file without root + * + * @test + */ + public function canNotRename() + { + $this->assertFalse(@rename(vfsStream::url('foo'), vfsStream::url('bar'))); + } +} +?> diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamZipTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamZipTestCase.php new file mode 100644 index 00000000000..45114c23059 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/vfsStreamZipTestCase.php @@ -0,0 +1,53 @@ +markTestSkipped('No ext/zip installed, skipping test.'); + } + + $this->markTestSkipped('Zip extension can not work with vfsStream urls.'); + + vfsStreamWrapper::register(); + vfsStreamWrapper::setRoot(vfsStream::newDirectory('root')); + + } + + /** + * @test + */ + public function createZipArchive() + { + $zip = new ZipArchive(); + $this->assertTrue($zip->open(vfsStream::url('root/test.zip'), ZIPARCHIVE::CREATE)); + $this->assertTrue($zip->addFromString("testfile1.txt", "#1 This is a test string added as testfile1.txt.\n")); + $this->assertTrue($zip->addFromString("testfile2.txt", "#2 This is a test string added as testfile2.txt.\n")); + $zip->setArchiveComment('a test'); + var_dump($zip); + $this->assertTrue($zip->close()); + var_dump($zip->getStatusString()); + var_dump($zip->close()); + var_dump($zip->getStatusString()); + var_dump($zip); + var_dump(file_exists(vfsStream::url('root/test.zip'))); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamAbstractVisitorTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamAbstractVisitorTestCase.php new file mode 100644 index 00000000000..bc8eb7d5571 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamAbstractVisitorTestCase.php @@ -0,0 +1,82 @@ +abstractVisitor = $this->getMock('org\\bovigo\\vfs\\visitor\\vfsStreamAbstractVisitor', + array('visitFile', 'visitDirectory') + ); + } + + /** + * @test + * @expectedException \InvalidArgumentException + */ + public function visitThrowsInvalidArgumentExceptionOnUnknownContentType() + { + $mockContent = $this->getMock('org\\bovigo\\vfs\\vfsStreamContent'); + $mockContent->expects($this->any()) + ->method('getType') + ->will($this->returnValue('invalid')); + $this->assertSame($this->abstractVisitor, + $this->abstractVisitor->visit($mockContent) + ); + } + + /** + * @test + */ + public function visitWithFileCallsVisitFile() + { + $file = new vfsStreamFile('foo.txt'); + $this->abstractVisitor->expects($this->once()) + ->method('visitFile') + ->with($this->equalTo($file)); + $this->assertSame($this->abstractVisitor, + $this->abstractVisitor->visit($file) + ); + } + + /** + * @test + */ + public function visitWithDirectoryCallsVisitDirectory() + { + $dir = new vfsStreamDirectory('bar'); + $this->abstractVisitor->expects($this->once()) + ->method('visitDirectory') + ->with($this->equalTo($dir)); + $this->assertSame($this->abstractVisitor, + $this->abstractVisitor->visit($dir) + ); + } +} +?> diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamPrintVisitorTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamPrintVisitorTestCase.php new file mode 100644 index 00000000000..f0a5fe851a9 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamPrintVisitorTestCase.php @@ -0,0 +1,89 @@ +at(vfsStream::setup()); + $printVisitor = new vfsStreamPrintVisitor(fopen('vfs://root/foo.txt', 'wb')); + $this->assertSame($printVisitor, + $printVisitor->visitFile(vfsStream::newFile('bar.txt')) + ); + $this->assertEquals("- bar.txt\n", $output->getContent()); + } + + /** + * @test + */ + public function visitDirectoryWritesDirectoryNameToStream() + { + $output = vfsStream::newFile('foo.txt') + ->at(vfsStream::setup()); + $printVisitor = new vfsStreamPrintVisitor(fopen('vfs://root/foo.txt', 'wb')); + $this->assertSame($printVisitor, + $printVisitor->visitDirectory(vfsStream::newDirectory('baz')) + ); + $this->assertEquals("- baz\n", $output->getContent()); + } + + /** + * @test + */ + public function visitRecursiveDirectoryStructure() + { + $root = vfsStream::setup('root', + null, + array('test' => array('foo' => array('test.txt' => 'hello'), + 'baz.txt' => 'world' + ), + 'foo.txt' => '' + ) + ); + $printVisitor = new vfsStreamPrintVisitor(fopen('vfs://root/foo.txt', 'wb')); + $this->assertSame($printVisitor, + $printVisitor->visitDirectory($root) + ); + $this->assertEquals("- root\n - test\n - foo\n - test.txt\n - baz.txt\n - foo.txt\n", file_get_contents('vfs://root/foo.txt')); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamStructureVisitorTestCase.php b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamStructureVisitorTestCase.php new file mode 100644 index 00000000000..633b1a9e032 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/php/org/bovigo/vfs/visitor/vfsStreamStructureVisitorTestCase.php @@ -0,0 +1,72 @@ +assertEquals(array('foo.txt' => 'test'), + $structureVisitor->visitFile(vfsStream::newFile('foo.txt') + ->withContent('test') + ) + ->getStructure() + ); + } + + /** + * @test + */ + public function visitDirectoryCreatesStructureForDirectory() + { + $structureVisitor = new vfsStreamStructureVisitor(); + $this->assertEquals(array('baz' => array()), + $structureVisitor->visitDirectory(vfsStream::newDirectory('baz')) + ->getStructure() + ); + } + + /** + * @test + */ + public function visitRecursiveDirectoryStructure() + { + $root = vfsStream::setup('root', + null, + array('test' => array('foo' => array('test.txt' => 'hello'), + 'baz.txt' => 'world' + ), + 'foo.txt' => '' + ) + ); + $structureVisitor = new vfsStreamStructureVisitor(); + $this->assertEquals(array('root' => array('test' => array('foo' => array('test.txt' => 'hello'), + 'baz.txt' => 'world' + ), + 'foo.txt' => '' + ), + ), + $structureVisitor->visitDirectory($root) + ->getStructure() + ); + } +} +?> \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/emptyFolder/.gitignore b/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/emptyFolder/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/aFile.txt b/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/aFile.txt new file mode 100644 index 00000000000..19102815663 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/aFile.txt @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/subfolder1/file1.txt b/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/subfolder1/file1.txt new file mode 100644 index 00000000000..f6ea0495187 --- /dev/null +++ b/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/subfolder1/file1.txt @@ -0,0 +1 @@ +foobar \ No newline at end of file diff --git a/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/subfolder2/.gitignore b/vendor/mikey179/vfsStream/src/test/resources/filesystemcopy/withSubfolders/subfolder2/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/vendor/rmccue/requests/.coveralls.yml b/vendor/rmccue/requests/.coveralls.yml new file mode 100644 index 00000000000..243fef67ac5 --- /dev/null +++ b/vendor/rmccue/requests/.coveralls.yml @@ -0,0 +1,4 @@ +src_dir: library +coverage_clover: tests/clover.xml +json_path: tests/coveralls.json +service_name: travis-ci \ No newline at end of file diff --git a/vendor/rmccue/requests/.travis.yml b/vendor/rmccue/requests/.travis.yml new file mode 100644 index 00000000000..6db28d2043d --- /dev/null +++ b/vendor/rmccue/requests/.travis.yml @@ -0,0 +1,19 @@ +language: php +before_script: + - phpenv local 5.4 + - composer install --dev --no-interaction + - phpenv local --unset + - cd tests +script: + - phpunit --coverage-clover clover.xml +after_script: + - cd .. + - phpenv local 5.4 + - php vendor/bin/coveralls -v + - phpenv local --unset +php: + - 5.2 + - 5.3 + - 5.4 + - 5.5 + - hhvm diff --git a/vendor/rmccue/requests/CHANGELOG.md b/vendor/rmccue/requests/CHANGELOG.md new file mode 100644 index 00000000000..2f2d1069743 --- /dev/null +++ b/vendor/rmccue/requests/CHANGELOG.md @@ -0,0 +1,56 @@ +Changelog +========= + +1.6.1 +----- +- [Fix compatibility with HHVM][#121] - Using HHVM with Requests would + previously cause either exceptions with SSL or segfaults with the cURL + handler. Props Ozh for his work here. + +[#121]: https://github.com/rmccue/Requests/issues/121 + +1.6.0 +----- +- [Add multiple request support][#23] - Send multiple HTTP requests with both + fsockopen and cURL, transparently falling back to synchronous when + not supported. + +- [Add proxy support][#70] - HTTP proxies are now natively supported via a + [high-level API][docs/proxy]. Major props to Ozh for his fantastic work + on this. + +- [Verify host name for SSL requests][#63] - Requests is now the first and only + standalone HTTP library to fully verify SSL hostnames even with socket + connections. Thanks to Michael Adams, Dion Hulse, Jon Cave, and Pádraic Brady + for reviewing the crucial code behind this. + +- [Add cookie support][#64] - Adds built-in support for cookies (built entirely + as a high-level API) + +- [Add sessions][#62] - To compliment cookies, [sessions][docs/usage-advanced] + can be created with a base URL and default options, plus a shared cookie jar. + +- Add [PUT][#1], [DELETE][#3], and [PATCH][#2] request support + +- [Add Composer support][#6] - You can now install Requests via the + `rmccue/requests` package on Composer + +[docs/proxy]: http://requests.ryanmccue.info/docs/proxy.html +[docs/usage-advanced]: http://requests.ryanmccue.info/docs/usage-advanced.html + +[#1]: https://github.com/rmccue/Requests/issues/1 +[#2]: https://github.com/rmccue/Requests/issues/2 +[#3]: https://github.com/rmccue/Requests/issues/3 +[#6]: https://github.com/rmccue/Requests/issues/6 +[#9]: https://github.com/rmccue/Requests/issues/9 +[#23]: https://github.com/rmccue/Requests/issues/23 +[#62]: https://github.com/rmccue/Requests/issues/62 +[#63]: https://github.com/rmccue/Requests/issues/63 +[#64]: https://github.com/rmccue/Requests/issues/64 +[#70]: https://github.com/rmccue/Requests/issues/70 + +[View all changes][https://github.com/rmccue/Requests/compare/v1.5.0...v1.6.0] + +1.5.0 +----- +Initial release! \ No newline at end of file diff --git a/vendor/rmccue/requests/LICENSE b/vendor/rmccue/requests/LICENSE new file mode 100644 index 00000000000..d61ae7b14f3 --- /dev/null +++ b/vendor/rmccue/requests/LICENSE @@ -0,0 +1,49 @@ +Requests +======== + +Copyright (c) 2010-2012 Ryan McCue and contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +ComplexPie IRI Parser +===================== + +Copyright (c) 2007-2010, Geoffrey Sneddon and Steve Minutillo. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the SimplePie Team nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/rmccue/requests/README.md b/vendor/rmccue/requests/README.md new file mode 100644 index 00000000000..76dc87db6c0 --- /dev/null +++ b/vendor/rmccue/requests/README.md @@ -0,0 +1,146 @@ +Requests for PHP +================ + +Requests is a HTTP library written in PHP, for human beings. It is roughly +based on the API from the excellent [Requests Python +library](http://python-requests.org/). Requests is [ISC +Licensed](https://github.com/rmccue/Requests/blob/master/LICENSE) (similar to +the new BSD license) and has no dependencies, except for PHP 5.2+. + +Despite PHP's use as a language for the web, its tools for sending HTTP requests +are severely lacking. cURL has an +[interesting API](http://php.net/manual/en/function.curl-setopt.php), to say the +least, and you can't always rely on it being available. Sockets provide only low +level access, and require you to build most of the HTTP response parsing +yourself. + +We all have better things to do. That's why Requests was born. + +```php +$headers = array('Accept' => 'application/json'); +$options = array('auth' => array('user', 'pass')); +$request = Requests::get('https://api.github.com/gists', $headers, $options); + +var_dump($request->status_code); +// int(200) + +var_dump($request->headers['content-type']); +// string(31) "application/json; charset=utf-8" + +var_dump($request->body); +// string(26891) "[...]" +``` + +Requests allows you to send **HEAD**, **GET**, **POST**, **PUT**, **DELETE**, +and **PATCH** HTTP requests. You can add headers, form data, multipart files, +and parameters with simple arrays, and access the response data in the same way. +Requests uses cURL and fsockopen, depending on what your system has available, +but abstracts all the nasty stuff out of your way, providing a consistent API. + + +Features +-------- + +- International Domains and URLs +- Browser-style SSL Verification +- Basic/Digest Authentication +- Automatic Decompression +- Connection Timeouts + + +Installation +------------ + +### Install with Composer +If you're using [Composer](https://github.com/composer/composer) to manage +dependencies, you can add Requests with it. + + { + "require": { + "rmccue/requests": ">=1.0" + }, + "autoload": { + "psr-0": {"Requests": "library/"} + } + } + +### Install source from GitHub +To install the source code: + + $ git clone git://github.com/rmccue/Requests.git + +And include it in your scripts: + + require_once '/path/to/Requests/library/Requests.php'; + +You'll probably also want to register an autoloader: + + Requests::register_autoloader(); + + +### Install source from zip/tarball +Alternatively, you can fetch a [tarball][] or [zipball][]: + + $ curl -L https://github.com/rmccue/Requests/tarball/master | tar xzv + (or) + $ wget https://github.com/rmccue/Requests/tarball/master -O - | tar xzv + +[tarball]: https://github.com/rmccue/Requests/tarball/master +[zipball]: https://github.com/rmccue/Requests/zipball/master + + +### Using a Class Loader +If you're using a class loader (e.g., [Symfony Class Loader][]) for +[PSR-0][]-style class loading: + + $loader->registerPrefix('Requests', 'path/to/vendor/Requests/library'); + +[Symfony Class Loader]: https://github.com/symfony/ClassLoader +[PSR-0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + + +Documentation +------------- +The best place to start is our [prose-based documentation][], which will guide +you through using Requests. + +After that, take a look at [the documentation for +`Requests::request()`][request_method], where all the parameters are fully +documented. + +Requests is [100% documented with PHPDoc](http://requests.ryanmccue.info/api/). +If you find any problems with it, [create a new +issue](https://github.com/rmccue/Requests/issues/new)! + +[prose-based documentation]: https://github.com/rmccue/Requests/blob/master/docs/README.md +[request_method]: http://requests.ryanmccue.info/api/class-Requests.html#_request + +Testing +------- +[![Build Status](https://secure.travis-ci.org/rmccue/Requests.png?branch=master)](http://travis-ci.org/rmccue/Requests) +[![Coverage Status](https://coveralls.io/repos/rmccue/Requests/badge.png?branch=master)][coveralls] + +Requests strives to have 100% code-coverage of the library with an extensive +set of tests. We're not quite there yet, but [we're getting close][coveralls]. + +[coveralls]: https://coveralls.io/r/rmccue/Requests?branch=master + +To run the test suite, simply: + + $ cd tests + $ phpunit + +If you'd like to run a single set of tests, specify just the name: + + $ phpunit Transport/cURL + +Contribute +---------- + +1. Check for open issues or open a new issue for a feature request or a bug +2. Fork [the repository][] on Github to start making your changes to the + `master` branch (or branch off of it) +3. Write a test which shows that the bug was fixed or that the feature works as expected +4. Send a pull request and bug me until I merge it + +[the repository]: https://github.com/rmccue/Requests diff --git a/vendor/rmccue/requests/bin/create_pear_package.php b/vendor/rmccue/requests/bin/create_pear_package.php new file mode 100644 index 00000000000..32677092953 --- /dev/null +++ b/vendor/rmccue/requests/bin/create_pear_package.php @@ -0,0 +1,55 @@ + ' . PHP_EOL; + echo PHP_EOL; + echo ' version:' . PHP_EOL; + echo ' Version of the package, in the form of major.minor.bug' . PHP_EOL; + echo PHP_EOL; + echo ' stability:' . PHP_EOL; + echo ' One of alpha, beta, stable' . PHP_EOL; + die(); +} + +if (!isset($argv[2])) { + die('You must provide the stability (alpha, beta, or stable)'); +} + +$context = array( + 'date' => gmdate('Y-m-d'), + 'time' => gmdate('H:m:00'), + 'version' => $argv[1], + 'api_version' => $argv[1], + 'stability' => $argv[2], + 'api_stability' => $argv[2], +); + +$context['files'] = ''; +$path = realpath(dirname(__FILE__).'/../library/Requests'); +foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if (preg_match('/\.php$/', $file)) { + $name = str_replace($path . DIRECTORY_SEPARATOR, '', $file); + $name = str_replace(DIRECTORY_SEPARATOR, '/', $name); + $context['files'][] = "\t\t\t\t\t" . ''; + } +} + +$context['files'] = implode("\n", $context['files']); + +$template = file_get_contents(dirname(__FILE__).'/../package.xml.tpl'); +$content = preg_replace_callback('/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/', 'replace_parameters', $template); +file_put_contents(dirname(__FILE__).'/../package.xml', $content); + +function replace_parameters($matches) { + global $context; + + return isset($context[$matches[1]]) ? $context[$matches[1]] : null; +} diff --git a/vendor/rmccue/requests/composer.json b/vendor/rmccue/requests/composer.json new file mode 100644 index 00000000000..9baeb529743 --- /dev/null +++ b/vendor/rmccue/requests/composer.json @@ -0,0 +1,23 @@ +{ + "name": "rmccue/requests", + "description": "A HTTP library written in PHP, for human beings.", + "homepage": "http://github.com/rmccue/Requests", + "license": "ISC", + "keywords": ["http", "idna", "iri", "ipv6", "curl", "sockets", "fsockopen"], + "authors": [ + { + "name": "Ryan McCue", + "homepage": "http://ryanmccue.info" + } + ], + "require": { + "php": ">=5.2" + }, + "require-dev": { + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "psr-0": {"Requests": "library/"} + } +} diff --git a/vendor/rmccue/requests/docs/README.md b/vendor/rmccue/requests/docs/README.md new file mode 100644 index 00000000000..8116963d667 --- /dev/null +++ b/vendor/rmccue/requests/docs/README.md @@ -0,0 +1,28 @@ +Documentation +============= + +If you're here, you're looking for documentation for Requests! The documents +here are prose; you might also want to check out the [API documentation][]. + +[API documentation]: http://requests.ryanmccue.info/api/ + +* Introduction + * [Goals][goals] + * [Why should I use Requests instead of X?][why-requests] +* Usage + * [Making a request][usage] + * [Advanced usage][usage-advanced] + * [Authenticating your request][authentication] +* Advanced Usage + * [Custom authentication][authentication-custom] + * [Requests through proxy][proxy] + * [Hooking system][hooks] + +[goals]: goals.md +[why-requests]: why-requests.md +[usage]: usage.md +[usage-advanced]: usage-advanced.md +[authentication]: authentication.md +[authentication-custom]: authentication-custom.md +[hooks]: hooks.md +[proxy]: proxy.md \ No newline at end of file diff --git a/vendor/rmccue/requests/docs/authentication-custom.md b/vendor/rmccue/requests/docs/authentication-custom.md new file mode 100644 index 00000000000..d145d21cfe1 --- /dev/null +++ b/vendor/rmccue/requests/docs/authentication-custom.md @@ -0,0 +1,44 @@ +Custom Authentication +===================== +Custom authentication handlers are designed to be extremely simple to write. +In order to write a handler, you'll need to implement the `Requests_Auth` +interface. + +An instance of this handler is then passed in by the user via the `auth` +option, just like for normal authentication. + +Let's say we have a HTTP endpoint that checks for the `Hotdog` header and +authenticates you if said header is set to `Yummy`. (I don't know of any +services that do this; perhaps this is a market waiting to be tapped?) + +```php +class MySoftware_Auth_Hotdog implements Requests_Auth { + protected $password; + + public function __construct($password) { + $this->password = $password; + } + + public function register(Requests_Hooks &$hooks) { + $hooks->register('requests.before_request', array(&$this, 'before_request')); + } + + public function before_request(&$url, &$headers, &$data, &$type, &$options) { + $headers['Hotdog'] = $this->password; + } +} +``` + +We then use this in our request calls: + +``` +$options = array( + 'auth' => new MySoftware_Auth_Hotdog('yummy') +); +$response = Requests::get('http://hotdogbin.org/admin', array(), $options); +``` + +(For more information on how to register and use hooks, see the [hooking +system documentation][hooks]) + +[hooks]: hooks.md \ No newline at end of file diff --git a/vendor/rmccue/requests/docs/authentication.md b/vendor/rmccue/requests/docs/authentication.md new file mode 100644 index 00000000000..eaa7ea5d706 --- /dev/null +++ b/vendor/rmccue/requests/docs/authentication.md @@ -0,0 +1,31 @@ +Authentication +============== +Many requests that you make will require authentication of some type. Requests +includes support out of the box for HTTP Basic authentication, with more +built-ins coming soon. + +Making a Basic authenticated call is ridiculously easy: + +```php +$options = array( + 'auth' => new Requests_Auth_Basic(array('user', 'password')) +); +Requests::get('http://httpbin.org/basic-auth/user/password', array(), $options); +``` + +As Basic authentication is usually what you want when you specify a username +and password, you can also just pass in an array as a shorthand: + +```php +$options = array( + 'auth' => array('user', 'password') +); +Requests::get('http://httpbin.org/basic-auth/user/password', array(), $options); +``` + +Note that POST/PUT can also take a data parameter, so you also need that +before `$options`: + +```php +Requests::get('http://httpbin.org/basic-auth/user/password', array(), null, $options); +``` \ No newline at end of file diff --git a/vendor/rmccue/requests/docs/goals.md b/vendor/rmccue/requests/docs/goals.md new file mode 100644 index 00000000000..3275a3f0922 --- /dev/null +++ b/vendor/rmccue/requests/docs/goals.md @@ -0,0 +1,29 @@ +Goals +===== + +1. **Simple interface** + + Requests is designed to provide a simple, unified interface to making + requests, regardless of what is available on the system. This means not worrying. + +2. **Fully tested code** + + Requests strives to have 90%+ code coverage from the unit tests, aiming for + the ideal 100%. Introducing new features always means introducing new tests + + (Note: some parts of the code are not covered by design. These sections are + marked with `@codeCoverageIgnore` tags) + +3. **Maximum compatibility** + + No matter what you have installed on your system, you should be able to run + Requests. We use cURL if it's available, and fallback to sockets otherwise. + We require only a baseline of PHP 5.2, leaving the choice of PHP minimum + requirement fully in your hands, and giving you the ability to support many + more hosts. + +4. **No dependencies** + + Requests is designed to be entirely self-contained and doesn't require + anything else at all. You can run Requests on an entirely stock PHP build + without any additional extensions outside the standard library. diff --git a/vendor/rmccue/requests/docs/hooks.md b/vendor/rmccue/requests/docs/hooks.md new file mode 100644 index 00000000000..96a39bf4b9e --- /dev/null +++ b/vendor/rmccue/requests/docs/hooks.md @@ -0,0 +1,92 @@ +Hooks +===== +Requests has a hook system that you can use to manipulate parts of the request +process along with internal transport hooks. + +Check out the [API documentation for `Requests_Hooks`][requests_hooks] for more +information on how to use the hook system. + +Available Hooks +--------------- + +* `requests.before_request` + + Alter the request before it's sent to the transport. + + Parameters: `string &$url`, `array &$headers`, `array|string &$data`, + `string &$type`, `array &$options` + +* `requests.before_parse` + + Alter the raw HTTP response before parsing + + Parameters: `string &$response` + +* `requests.after_request` + + Alter the response object before it's returned to the user + + Parameters: `Requests_Response &$return` + +* `curl.before_request` + + Set cURL options before the transport sets any (note that Requests may + override these) + + Parameters: `cURL resource &$fp` + +* `curl.before_send` + + Set cURL options just before the request is actually sent via `curl_exec` + + Parameters: `cURL resource &$fp` + +* `curl.after_request` + + Alter the raw HTTP response before returning for parsing + + Parameters: `string &$response` + +* `fsockopen.before_request` + + Run events before the transport does anything + +* `fsockopen.after_headers` + + Add extra headers before the body begins (i.e. before `\r\n\r\n`) + + Parameters: `string &$out` + +* `fsockopen.before_send` + + Add body data before sending the request + + Parameters: `string &$out` + +* `fsockopen.after_send` + + Run events after writing the data to the socket + +* `fsockopen.after_request` + + Alter the raw HTTP response before returning for parsing + + Parameters: `string &$response` + + +Registering Hooks +----------------- +Note: if you're doing this in an authentication handler, see the [Custom +Authentication guide][authentication-custom] instead. + +[authentication-custom]: authentication-custom.md + +In order to register your own hooks, you need to instantiate `Requests_hooks` +and pass this in via the 'hooks' option. + +```php +$hooks = new Requests_Hooks(); +$hooks->register('requests.after_request', 'mycallback'); + +$request = Requests::get('http://httpbin.org/get', array(), array('hooks' => $hooks)); +``` \ No newline at end of file diff --git a/vendor/rmccue/requests/docs/proxy.md b/vendor/rmccue/requests/docs/proxy.md new file mode 100644 index 00000000000..cb2675de2f7 --- /dev/null +++ b/vendor/rmccue/requests/docs/proxy.md @@ -0,0 +1,23 @@ +Proxy Support +============= + +You can easily make requests through HTTP proxies. + +To make requests through an open proxy, specify the following options: + +```php +$options = array( + 'proxy' => '127.0.0.1:3128' +); +Requests::get('http://httpbin.org/ip', array(), $options); +``` + +If your proxy needs you to authenticate, the option will become an array like +the following: + +```php +$options = array( + 'proxy' => array( '127.0.0.1:3128', 'my_username', 'my_password' ) +); +Requests::get('http://httpbin.org/ip', array(), $options); +``` diff --git a/vendor/rmccue/requests/docs/usage-advanced.md b/vendor/rmccue/requests/docs/usage-advanced.md new file mode 100644 index 00000000000..b3f2880852f --- /dev/null +++ b/vendor/rmccue/requests/docs/usage-advanced.md @@ -0,0 +1,74 @@ +Advanced Usage +============== + +Session Handling +---------------- +Making multiple requests to the same site with similar options can be a pain, +since you end up repeating yourself. The Session object can be used to set +default parameters for these. + +Let's simulate communicating with GitHub. + +```php +$session = new Requests_Session('https://api.github.com/'); +$session->headers['X-ContactAuthor'] = 'rmccue'; +$session->useragent = 'My-Awesome-App'; + +$response = $session->get('/zen'); +``` + +You can use the `url`, `headers`, `data` and `options` properties of the Session +object to set the defaults for this session, and the constructor also takes +parameters in the same order as `Requests::request()`. Accessing any other +properties will set the corresponding key in the options array; that is: + +```php +// Setting the property... +$session->useragent = 'My-Awesome-App'; + +// ...is the same as setting the option +$session->options['useragent'] = 'My-Awesome-App'; +``` + + +Secure Requests with SSL +------------------------ +By default, HTTPS requests will use the most secure options available: + +```php +$response = Requests::get('https://httpbin.org/'); +``` + +Requests bundles certificates from the [Mozilla certificate authority list][], +which is the same list of root certificates used in most browsers. If you're +accessing sites with certificates from other CAs, or self-signed certificates, +you can point Requests to a custom CA list in PEM form (the same format +accepted by cURL and OpenSSL): + +```php +$options = array( + 'verify' => '/path/to/cacert.pem' +); +$response = Requests::get('https://httpbin.org/', array(), $options); +``` + +Alternatively, if you want to disable verification completely, this is possible +with `'verify' => false`, but note that this is extremely insecure and should be +avoided. + +### Security Note +Requests supports SSL across both cURL and fsockopen in a transparent manner. +Unlike other PHP HTTP libraries, support for verifying the certificate name is +built-in; that is, a request for `https://github.com/` will actually verify the +certificate's name even with the fsockopen transport. This makes Requests the +first and currently only PHP HTTP library that supports full SSL verification. + +(Note that WordPress now also supports this verification, thanks to efforts by +the Requests development team.) + +(See also the [related PHP][php-bug-47030] and [OpenSSL-related][php-bug-55820] +bugs in PHP for more information on Subject Alternate Name field.) + +[Mozilla certificate authority list]: http://www.mozilla.org/projects/security/certs/ +[php-bug-47030]: https://bugs.php.net/bug.php?id=47030 +[php-bug-55820]:https://bugs.php.net/bug.php?id=55820 diff --git a/vendor/rmccue/requests/docs/usage.md b/vendor/rmccue/requests/docs/usage.md new file mode 100644 index 00000000000..53ca4956cf9 --- /dev/null +++ b/vendor/rmccue/requests/docs/usage.md @@ -0,0 +1,154 @@ +Usage +===== + +Ready to go? Make sure you have Requests installed before attempting any of the +steps in this guide. + + +Loading Requests +---------------- +Before we can load Requests up, we'll need to make sure it's loaded. This is a +simple two-step: + +```php +// First, include Requests +include('/path/to/library/Requests.php'); + +// Next, make sure Requests can load internal classes +Requests::register_autoloader(); +``` + +If you'd like to bring along your own autoloader, you can forget about this +completely. + + +Make a GET Request +------------------ +One of the most basic things you can do with HTTP is make a GET request. + +Let's grab GitHub's public timeline: + +```php +$response = Requests::get('https://github.com/timeline.json'); +``` + +`$response` is now a **Requests_Response** object. Response objects are what +you'll be working with whenever you want to get data back from your request. + + +Using the Response Object +------------------------- +Now that we have the response from GitHub, let's get the body of the response. + +```php +var_dump($response->body); +// string(42865) "[{"repository":{"url":"... +``` + + +Custom Headers +-------------- +If you want to add custom headers to the request, simply pass them in as an +associative array as the second parameter: + +```php +$response = Requests::get('https://github.com/timeline.json', array('X-Requests' => 'Is Awesome!')); +``` + + +Make a POST Request +------------------- +Making a POST request is very similar to making a GET: + +```php +$response = Requests::post('http://httpbin.org/post'); +``` + +You'll probably also want to pass in some data. You can pass in either a +string, an array or an object (Requests uses [`http_build_query`][build_query] +internally) as the third parameter (after the URL and headers): + +[build_query]: http://php.net/http_build_query + +```php +$data = array('key1' => 'value1', 'key2' => 'value2'); +$response = Requests::post('http://httpbin.org/post', array(), $data); +var_dump($response->body); +``` + +This gives the output: + + string(503) "{ + "origin": "124.191.162.147", + "files": {}, + "form": { + "key2": "value2", + "key1": "value1" + }, + "headers": { + "Content-Length": "23", + "Accept-Encoding": "deflate;q=1.0, compress;q=0.5, gzip;q=0.5", + "X-Forwarded-Port": "80", + "Connection": "keep-alive", + "User-Agent": "php-requests/1.6-dev", + "Host": "httpbin.org", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" + }, + "url": "http://httpbin.org/post", + "args": {}, + "data": "" + }" + +To send raw data, simply pass in a string instead. You'll probably also want to +set the Content-Type header to ensure the remote server knows what you're +sending it: + +```php +$url = 'https://api.github.com/some/endpoint'; +$headers = array('Content-Type' => 'application/json'); +$data = array('some' => 'data'); +$response = Requests::post($url, $headers, json_encode($data)); +``` + +Note that if you don't manually specify a Content-Type header, Requests has +undefined behaviour for the header. It may be set to various values depending +on the internal execution path, so it's recommended to set this explicitly if +you need to. + + +Status Codes +------------ +The Response object also gives you access to the status code: + +```php +var_dump($response->status_code); +// int(200) +``` + +You can also easily check if this status code is a success code, or if it's an +error: + +```php +var_dump($response->success); +// bool(true) +``` + + +Response Headers +---------------- +We can also grab headers pretty easily: + +```php +var_dump($response->headers['Date']); +// string(29) "Thu, 09 Feb 2012 15:22:06 GMT" +``` + +Note that this is case-insensitive, so the following are all equivalent: + +* `$response->headers['Date']` +* `$response->headers['date']` +* `$response->headers['DATE']` +* `$response->headers['dAtE']` + +If a header isn't set, this will give `null`. You can also check with +`isset($response->headers['date'])` diff --git a/vendor/rmccue/requests/docs/why-requests.md b/vendor/rmccue/requests/docs/why-requests.md new file mode 100644 index 00000000000..28747313b64 --- /dev/null +++ b/vendor/rmccue/requests/docs/why-requests.md @@ -0,0 +1,192 @@ +Why Requests Instead of X? +========================== +This is a quick look at why you should use Requests instead of another +solution. Keep in mind though that these are my point of view, and they may not +be issues for you. + +As always with software, you should choose what you think is best. + + +Why should I use Requests? +-------------------------- +1. **Designed for maximum compatibility** + + The realities of working with widely deployable software mean that awesome + PHP features aren't always available. PHP 5.3, cURL, OpenSSL and more are not + necessarily going to be available on every system. While you're welcome to + require PHP 5.3, 5.4 or even 5.5, it's not our job to force you to use those. + + (The WordPress project estimates [about 60%][wpstats] of hosts are running + PHP 5.2, so this is a serious issue for developers working on large + deployable projects.) + + Don't worry though, Requests will automatically use better features where + possible, giving you an extra speed boost with cURL. + +2. **Simple API** + + Requests' API is designed to be able to be learnt in 10 minutes. Everything + from basic requests all the way up to advanced usage involving custom SSL + certificates and stored cookies is handled by a simple API. + + Other HTTP libraries optimize for the library developer's time; **Requests + optimizes for your time**. + +3. **Thoroughly tested** + + Requests is [continuously integrated with Travis][travis] and test coverage + is [constantly monitored with Coveralls][coveralls] to give you confidence in + the library. We aim for test coverage **over 90%** at all times, and new + features require new tests to go along with them. This ensures that you can + be confident in the quality of the code, as well as being able to update to + the latest version of Requests without worrying about compatibility. + +4. **Secure by default** + + Unlike other HTTP libraries, Requests is secure by default. Requests is the + **first and currently only** standalone HTTP library to + **[fully verify][requests_ssl] all HTTPS requests** even without cURL. We + also bundle the latest root certificate authorities to ensure that your + secure requests are actually secure. + + (Of note is that WordPress as of version 3.7 also supports full checking of + the certificates, thanks to [evangelism efforts on our behalf][wpssl]. + Together, we are the only HTTP libraries in PHP to fully verify certificates + to the same level as browsers.) + +5. **Extensible from the core** + + If you need low-level access to Requests' internals, simply plug your + callbacks in via the built-in [hooking system][] and mess around as much as + you want. Requests' simple hooking system is so powerful that both + authentication handlers and cookie support is actually handled internally + with hooks. + +[coveralls]: https://coveralls.io/r/rmccue/Requests +[hooking system]: hooks.md +[requests_ssl]: https://github.com/rmccue/Requests/blob/master/library/Requests/SSL.php +[travis]: https://travis-ci.org/rmccue/Requests +[wpssl]: http://core.trac.wordpress.org/ticket/25007 + + +Why shouldn't I use... +---------------------- +Requests isn't the first or only HTTP library in PHP and it's important to +acknowledge the other solutions out there. Here's why you should use Requests +instead of something else, in our opinion. + + +### cURL + +1. **Not every host has cURL installed** + + cURL is far from being ubiquitous, so you can't rely on it always being + available when distributing software. Anecdotal data collected from various + projects indicates that cURL is available on roughly 90% of hosts, but that + leaves 10% of hosts without it. + +2. **cURL's interface sucks** + + cURL's interface was designed for PHP 4, and hence uses resources with + horrible functions such as `curl_setopt()`. Combined with that, it uses 229 + global constants, polluting the global namespace horribly. + + Requests, on the other hand, exposes only a handful of classes to the + global namespace, most of which are for internal use. You can learn to use + the `Requests::request()` method and the `Requests_Response` object in the + space of 10 minutes and you already know how to use Requests. + + +### Guzzle + +1. **Requires cURL and PHP 5.3+** + + Guzzle is designed to be a client to fit a large number of installations, but + as a result of optimizing for Guzzle developer time, it uses cURL as an + underlying transport. As noted above, this is a majority of systems, but + far from all. + + The same is true for PHP 5.3+. While we'd all love to rely on PHP's newer + features, the fact is that a huge percentage of hosts are still running on + PHP 5.2. (The WordPress project estimates [about 60%][wpstats] of hosts are + running PHP 5.2.) + +2. **Not just a HTTP client** + + Guzzle is not intended to just be a HTTP client, but rather to be a + full-featured REST client. Requests is just a HTTP client, intentionally. Our + development strategy is to act as a low-level library that REST clients can + easily be built on, not to provide the whole kitchen sink for you. + + If you want to rapidly develop a web service client using a framework, Guzzle + will suit you perfectly. On the other hand, if you want a HTTP client without + all the rest, Requests is the way to go. + +[wpstats]: http://wordpress.org/about/stats/ + + +### Buzz + +1. **Requires PHP 5.3+** + + As with Guzzle, while PHP 5.3+ is awesome, you can't always rely on it being + on a host. With widely distributable software, this is a huge problem. + +2. **Not transport-transparent** + + For making certain types of requests, such as multi-requests, you can't rely + on a high-level abstraction and instead have to use the low-level transports. + This really gains nothing (other than a fancy interface) over just using the + methods directly and means that you can't rely on features to be available. + + +### fsockopen + +1. **Very low-level** + + fsockopen is used for working with sockets directly, so it only knows about + the transport layer (TCP in our case), not anything higher (i.e. HTTP on the + application layer). To be able to use fsockopen as a HTTP client, you need + to write all the HTTP code yourself, and once you're done, you'll end up + with something that is almost exactly like Requests. + + +### PEAR HTTP_Request2 + +1. **Requires PEAR** + + PEAR is (in theory) a great distribution system (with a less than wonderful + implementation), however it is not ubiquitous, as many hosts disable it to + save on space that most people aren't going to use anyway. + + PEAR is also a pain for users. Users want to be able to download a zip of + your project without needing to install anything else from PEAR. + + (If you really want though, Requests is available via PEAR. Check the README + to see how to grab it.) + +2. **Depends on other PEAR utilities** + + HTTP\_Request2 requires Net_URL2 in order to function, locking you in to + using PEAR for your project. + + Requests is entirely self-contained, and includes all the libraries it needs + (for example, Requests\_IRI is based on ComplexPie\_IRI by Geoffrey Sneddon). + + +### PECL HttpRequest + +1. **Requires a PECL extension** + + Similar to PEAR, users aren't big fans of installing extra libraries. Unlike + PEAR though, PECL extensions require compiling, which end users will be + unfamiliar with. In addition, on systems where users do not have full + control over PHP, they will be unable to install custom extensions. + + +### Zend Framework's Zend\_Http\_Client + +1. **Requires other parts of the Zend Framework** + + Similar to HTTP_Request2, Zend's client is not fully self-contained and + requires other components from the framework. diff --git a/vendor/rmccue/requests/examples/basic-auth.php b/vendor/rmccue/requests/examples/basic-auth.php new file mode 100644 index 00000000000..a9584a8ab8b --- /dev/null +++ b/vendor/rmccue/requests/examples/basic-auth.php @@ -0,0 +1,16 @@ + array('someuser', 'password') +); +$request = Requests::get('http://httpbin.org/basic-auth/someuser/password', array(), $options); + +// Check what we received +var_dump($request); \ No newline at end of file diff --git a/vendor/rmccue/requests/examples/get.php b/vendor/rmccue/requests/examples/get.php new file mode 100644 index 00000000000..8d065372019 --- /dev/null +++ b/vendor/rmccue/requests/examples/get.php @@ -0,0 +1,13 @@ + 'application/json')); + +// Check what we received +var_dump($request); \ No newline at end of file diff --git a/vendor/rmccue/requests/examples/multiple.php b/vendor/rmccue/requests/examples/multiple.php new file mode 100644 index 00000000000..69757c3b103 --- /dev/null +++ b/vendor/rmccue/requests/examples/multiple.php @@ -0,0 +1,45 @@ + 'http://httpbin.org/get', + 'headers' => array('Accept' => 'application/javascript'), + ), + 'post' => array( + 'url' => 'http://httpbin.org/post', + 'data' => array('mydata' => 'something'), + ), + 'delayed' => array( + 'url' => 'http://httpbin.org/delay/10', + 'options' => array( + 'timeout' => 20, + ), + ), +); + +// Setup a callback +function my_callback(&$request, $id) { + var_dump($id, $request); +} + +// Tell Requests to use the callback +$options = array( + 'complete' => 'my_callback', +); + +// Send the request! +$responses = Requests::request_multiple($requests, $options); + +// Note: the response from the above call will be an associative array matching +// $requests with the response data, however we've already handled it in +// my_callback() anyway! +// +// If you don't believe me, uncomment this: +# var_dump($responses); \ No newline at end of file diff --git a/vendor/rmccue/requests/examples/post.php b/vendor/rmccue/requests/examples/post.php new file mode 100644 index 00000000000..5c9e2c7778f --- /dev/null +++ b/vendor/rmccue/requests/examples/post.php @@ -0,0 +1,13 @@ + 'something')); + +// Check what we received +var_dump($request); \ No newline at end of file diff --git a/vendor/rmccue/requests/examples/proxy.php b/vendor/rmccue/requests/examples/proxy.php new file mode 100644 index 00000000000..c8a2e94bca0 --- /dev/null +++ b/vendor/rmccue/requests/examples/proxy.php @@ -0,0 +1,18 @@ + '127.0.0.1:8080', // syntax: host:port, eg 12.13.14.14:8080 or someproxy.com:3128 + // If you need to authenticate, use the following syntax: + // 'proxy' => array( '127.0.0.1:8080', 'username', 'password' ), +); +$request = Requests::get('http://httpbin.org/ip', array(), $options ); + +// See result +var_dump($request->body); diff --git a/vendor/rmccue/requests/examples/session.php b/vendor/rmccue/requests/examples/session.php new file mode 100644 index 00000000000..ff44d240a21 --- /dev/null +++ b/vendor/rmccue/requests/examples/session.php @@ -0,0 +1,24 @@ +headers['Accept'] = 'application/json'; +$session->useragent = 'Awesomesauce'; + +// Now let's make a request! +$request = $session->get('/get'); + +// Check what we received +var_dump($request); + +// Let's check our user agent! +$request = $session->get('/user-agent'); + +// And check again +var_dump($request); diff --git a/vendor/rmccue/requests/library/Requests.php b/vendor/rmccue/requests/library/Requests.php new file mode 100644 index 00000000000..cc756410b0c --- /dev/null +++ b/vendor/rmccue/requests/library/Requests.php @@ -0,0 +1,863 @@ +dispatch('requests.before_request', array(&$url, &$headers, &$data, &$type, &$options)); + + if (!empty($options['transport'])) { + $transport = $options['transport']; + + if (is_string($options['transport'])) { + $transport = new $transport(); + } + } + else { + $transport = self::get_transport(); + } + $response = $transport->request($url, $headers, $data, $options); + + $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options)); + + return self::parse_response($response, $url, $headers, $data, $options); + } + + /** + * Send multiple HTTP requests simultaneously + * + * The `$requests` parameter takes an associative or indexed array of + * request fields. The key of each request can be used to match up the + * request with the returned data, or with the request passed into your + * `multiple.request.complete` callback. + * + * The request fields value is an associative array with the following keys: + * + * - `url`: Request URL Same as the `$url` parameter to + * {@see Requests::request} + * (string, required) + * - `headers`: Associative array of header fields. Same as the `$headers` + * parameter to {@see Requests::request} + * (array, default: `array()`) + * - `data`: Associative array of data fields or a string. Same as the + * `$data` parameter to {@see Requests::request} + * (array|string, default: `array()`) + * - `type`: HTTP request type (use Requests constants). Same as the `$type` + * parameter to {@see Requests::request} + * (string, default: `Requests::GET`) + * - `data`: Associative array of options. Same as the `$options` parameter + * to {@see Requests::request} + * (array, default: see {@see Requests::request}) + * - `cookies`: Associative array of cookie name to value, or cookie jar. + * (array|Requests_Cookie_Jar) + * + * If the `$options` parameter is specified, individual requests will + * inherit options from it. This can be used to use a single hooking system, + * or set all the types to `Requests::POST`, for example. + * + * In addition, the `$options` parameter takes the following global options: + * + * - `complete`: A callback for when a request is complete. Takes two + * parameters, a Requests_Response/Requests_Exception reference, and the + * ID from the request array (Note: this can also be overridden on a + * per-request basis, although that's a little silly) + * (callback) + * + * @param array $requests Requests data (see description for more information) + * @param array $options Global and default options (see {@see Requests::request}) + * @return array Responses (either Requests_Response or a Requests_Exception object) + */ + public static function request_multiple($requests, $options = array()) { + $options = array_merge(self::get_default_options(true), $options); + + if (!empty($options['hooks'])) { + $options['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple')); + if (!empty($options['complete'])) { + $options['hooks']->register('multiple.request.complete', $options['complete']); + } + } + + foreach ($requests as $id => &$request) { + if (!isset($request['headers'])) { + $request['headers'] = array(); + } + if (!isset($request['data'])) { + $request['data'] = array(); + } + if (!isset($request['type'])) { + $request['type'] = self::GET; + } + if (!isset($request['options'])) { + $request['options'] = $options; + $request['options']['type'] = $request['type']; + } + else { + if (empty($request['options']['type'])) { + $request['options']['type'] = $request['type']; + } + $request['options'] = array_merge($options, $request['options']); + } + + self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']); + + // Ensure we only hook in once + if ($request['options']['hooks'] !== $options['hooks']) { + $request['options']['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple')); + if (!empty($request['options']['complete'])) { + $request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']); + } + } + } + unset($request); + + if (!empty($options['transport'])) { + $transport = $options['transport']; + + if (is_string($options['transport'])) { + $transport = new $transport(); + } + } + else { + $transport = self::get_transport(); + } + $responses = $transport->request_multiple($requests, $options); + + foreach ($responses as $id => &$response) { + // If our hook got messed with somehow, ensure we end up with the + // correct response + if (is_string($response)) { + $request = $requests[$id]; + self::parse_multiple($response, $request); + $request['options']['hooks']->dispatch('multiple.request.complete', array(&$response, $id)); + } + } + + return $responses; + } + + /** + * Get the default options + * + * @see Requests::request() for values returned by this method + * @param boolean $multirequest Is this a multirequest? + * @return array Default option values + */ + protected static function get_default_options($multirequest = false) { + $defaults = array( + 'timeout' => 10, + 'useragent' => 'php-requests/' . self::VERSION, + 'redirected' => 0, + 'redirects' => 10, + 'follow_redirects' => true, + 'blocking' => true, + 'type' => self::GET, + 'filename' => false, + 'auth' => false, + 'proxy' => false, + 'cookies' => false, + 'idn' => true, + 'hooks' => null, + 'transport' => null, + 'verify' => dirname( __FILE__ ) . '/Requests/Transport/cacert.pem', + 'verifyname' => true, + ); + if ($multirequest !== false) { + $defaults['complete'] = null; + } + return $defaults; + } + + /** + * Set the default values + * + * @param string $url URL to request + * @param array $headers Extra headers to send with the request + * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests + * @param string $type HTTP request type + * @param array $options Options for the request + * @return array $options + */ + protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) { + if (!preg_match('/^http(s)?:\/\//i', $url)) { + throw new Requests_Exception('Only HTTP requests are handled.', 'nonhttp', $url); + } + + if (empty($options['hooks'])) { + $options['hooks'] = new Requests_Hooks(); + } + + if (is_array($options['auth'])) { + $options['auth'] = new Requests_Auth_Basic($options['auth']); + } + if ($options['auth'] !== false) { + $options['auth']->register($options['hooks']); + } + + if (!empty($options['proxy'])) { + $options['proxy'] = new Requests_Proxy_HTTP($options['proxy']); + } + if ($options['proxy'] !== false) { + $options['proxy']->register($options['hooks']); + } + + if (is_array($options['cookies'])) { + $options['cookies'] = new Requests_Cookie_Jar($options['cookies']); + } + elseif (empty($options['cookies'])) { + $options['cookies'] = new Requests_Cookie_Jar(); + } + if ($options['cookies'] !== false) { + $options['cookies']->register($options['hooks']); + } + + if ($options['idn'] !== false) { + $iri = new Requests_IRI($url); + $iri->host = Requests_IDNAEncoder::encode($iri->ihost); + $url = $iri->uri; + } + } + + /** + * HTTP response parser + * + * @throws Requests_Exception On missing head/body separator (`requests.no_crlf_separator`) + * @throws Requests_Exception On missing head/body separator (`noversion`) + * @throws Requests_Exception On missing head/body separator (`toomanyredirects`) + * + * @param string $headers Full response text including headers and body + * @param string $url Original request URL + * @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects + * @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects + * @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects + * @return Requests_Response + */ + protected static function parse_response($headers, $url, $req_headers, $req_data, $options) { + $return = new Requests_Response(); + if (!$options['blocking']) { + return $return; + } + + $return->raw = $headers; + $return->url = $url; + + if (!$options['filename']) { + if (($pos = strpos($headers, "\r\n\r\n")) === false) { + // Crap! + throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator'); + } + + $headers = substr($return->raw, 0, $pos); + $return->body = substr($return->raw, $pos + strlen("\n\r\n\r")); + } + else { + $return->body = ''; + } + // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3) + $headers = str_replace("\r\n", "\n", $headers); + // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2) + $headers = preg_replace('/\n[ \t]/', ' ', $headers); + $headers = explode("\n", $headers); + preg_match('#^HTTP/1\.\d[ \t]+(\d+)#i', array_shift($headers), $matches); + if (empty($matches)) { + throw new Requests_Exception('Response could not be parsed', 'noversion', $headers); + } + $return->status_code = (int) $matches[1]; + if ($return->status_code >= 200 && $return->status_code < 300) { + $return->success = true; + } + + foreach ($headers as $header) { + list($key, $value) = explode(':', $header, 2); + $value = trim($value); + preg_replace('#(\s+)#i', ' ', $value); + $return->headers[$key] = $value; + } + if (isset($return->headers['transfer-encoding'])) { + $return->body = self::decode_chunked($return->body); + unset($return->headers['transfer-encoding']); + } + if (isset($return->headers['content-encoding'])) { + $return->body = self::decompress($return->body); + } + + //fsockopen and cURL compatibility + if (isset($return->headers['connection'])) { + unset($return->headers['connection']); + } + + $options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options)); + + if ((in_array($return->status_code, array(300, 301, 302, 303, 307)) || $return->status_code > 307 && $return->status_code < 400) && $options['follow_redirects'] === true) { + if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) { + if ($return->status_code === 303) { + $options['type'] = Requests::GET; + } + $options['redirected']++; + $location = $return->headers['location']; + if (strpos ($location, '/') === 0) { + // relative redirect, for compatibility make it absolute + $location = Requests_IRI::absolutize($url, $location); + $location = $location->uri; + } + $redirected = self::request($location, $req_headers, $req_data, false, $options); + $redirected->history[] = $return; + return $redirected; + } + elseif ($options['redirected'] >= $options['redirects']) { + throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return); + } + } + + $return->redirects = $options['redirected']; + + $options['hooks']->dispatch('requests.after_request', array(&$return, $req_headers, $req_data, $options)); + return $return; + } + + /** + * Callback for `transport.internal.parse_response` + * + * Internal use only. Converts a raw HTTP response to a Requests_Response + * while still executing a multiple request. + * + * @param string $headers Full response text including headers and body + * @param array $request Request data as passed into {@see Requests::request_multiple()} + * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object + */ + public static function parse_multiple(&$response, $request) { + try { + $response = self::parse_response($response, $request['url'], $request['headers'], $request['data'], $request['options']); + } + catch (Requests_Exception $e) { + $response = $e; + } + } + + /** + * Decoded a chunked body as per RFC 2616 + * + * @see http://tools.ietf.org/html/rfc2616#section-3.6.1 + * @param string $data Chunked body + * @return string Decoded body + */ + protected static function decode_chunked($data) { + if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($data))) { + return $data; + } + + $decoded = ''; + $encoded = $data; + + while (true) { + $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches ); + if (!$is_chunked) { + // Looks like it's not chunked after all + return $data; + } + + $length = hexdec(trim($matches[1])); + if ($length === 0) { + // Ignore trailer headers + return $decoded; + } + + $chunk_length = strlen($matches[0]); + $decoded .= $part = substr($encoded, $chunk_length, $length); + $encoded = substr($encoded, $chunk_length + $length + 2); + + if (trim($encoded) === '0' || empty($encoded)) { + return $decoded; + } + } + + // We'll never actually get down here + // @codeCoverageIgnoreStart + } + // @codeCoverageIgnoreEnd + + /** + * Convert a key => value array to a 'key: value' array for headers + * + * @param array $array Dictionary of header values + * @return array List of headers + */ + public static function flatten($array) { + $return = array(); + foreach ($array as $key => $value) { + $return[] = "$key: $value"; + } + return $return; + } + + /** + * Convert a key => value array to a 'key: value' array for headers + * + * @deprecated Misspelling of {@see Requests::flatten} + * @param array $array Dictionary of header values + * @return array List of headers + */ + public static function flattern($array) { + return self::flatten($array); + } + + /** + * Decompress an encoded body + * + * Implements gzip, compress and deflate. Guesses which it is by attempting + * to decode. + * + * @todo Make this smarter by defaulting to whatever the headers say first + * @param string $data Compressed data in one of the above formats + * @return string Decompressed string + */ + public static function decompress($data) { + if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") { + // Not actually compressed. Probably cURL ruining this for us. + return $data; + } + + if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) { + return $decoded; + } + elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) { + return $decoded; + } + elseif (($decoded = self::compatible_gzinflate($data)) !== false) { + return $decoded; + } + elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) { + return $decoded; + } + + return $data; + } + + /** + * Decompression of deflated string while staying compatible with the majority of servers. + * + * Certain Servers will return deflated data with headers which PHP's gzinflate() + * function cannot handle out of the box. The following function has been created from + * various snippets on the gzinflate() PHP documentation. + * + * Warning: Magic numbers within. Due to the potential different formats that the compressed + * data may be returned in, some "magic offsets" are needed to ensure proper decompression + * takes place. For a simple progmatic way to determine the magic offset in use, see: + * http://core.trac.wordpress.org/ticket/18273 + * + * @since 2.8.1 + * @link http://core.trac.wordpress.org/ticket/18273 + * @link http://au2.php.net/manual/en/function.gzinflate.php#70875 + * @link http://au2.php.net/manual/en/function.gzinflate.php#77336 + * + * @param string $gzData String to decompress. + * @return string|bool False on failure. + */ + public static function compatible_gzinflate($gzData) { + // Compressed data might contain a full zlib header, if so strip it for + // gzinflate() + if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) { + $i = 10; + $flg = ord( substr($gzData, 3, 1) ); + if ( $flg > 0 ) { + if ( $flg & 4 ) { + list($xlen) = unpack('v', substr($gzData, $i, 2) ); + $i = $i + 2 + $xlen; + } + if ( $flg & 8 ) + $i = strpos($gzData, "\0", $i) + 1; + if ( $flg & 16 ) + $i = strpos($gzData, "\0", $i) + 1; + if ( $flg & 2 ) + $i = $i + 2; + } + $decompressed = self::compatible_gzinflate( substr( $gzData, $i ) ); + if ( false !== $decompressed ) { + return $decompressed; + } + } + + // If the data is Huffman Encoded, we must first strip the leading 2 + // byte Huffman marker for gzinflate() + // The response is Huffman coded by many compressors such as + // java.util.zip.Deflater, Ruby’s Zlib::Deflate, and .NET's + // System.IO.Compression.DeflateStream. + // + // See http://decompres.blogspot.com/ for a quick explanation of this + // data type + $huffman_encoded = false; + + // low nibble of first byte should be 0x08 + list( , $first_nibble ) = unpack( 'h', $gzData ); + + // First 2 bytes should be divisible by 0x1F + list( , $first_two_bytes ) = unpack( 'n', $gzData ); + + if ( 0x08 == $first_nibble && 0 == ( $first_two_bytes % 0x1F ) ) + $huffman_encoded = true; + + if ( $huffman_encoded ) { + if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) ) + return $decompressed; + } + + if ( "\x50\x4b\x03\x04" == substr( $gzData, 0, 4 ) ) { + // ZIP file format header + // Offset 6: 2 bytes, General-purpose field + // Offset 26: 2 bytes, filename length + // Offset 28: 2 bytes, optional field length + // Offset 30: Filename field, followed by optional field, followed + // immediately by data + list( , $general_purpose_flag ) = unpack( 'v', substr( $gzData, 6, 2 ) ); + + // If the file has been compressed on the fly, 0x08 bit is set of + // the general purpose field. We can use this to differentiate + // between a compressed document, and a ZIP file + $zip_compressed_on_the_fly = ( 0x08 == (0x08 & $general_purpose_flag ) ); + + if ( ! $zip_compressed_on_the_fly ) { + // Don't attempt to decode a compressed zip file + return $gzData; + } + + // Determine the first byte of data, based on the above ZIP header + // offsets: + $first_file_start = array_sum( unpack( 'v2', substr( $gzData, 26, 4 ) ) ); + if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 30 + $first_file_start ) ) ) ) { + return $decompressed; + } + return false; + } + + // Finally fall back to straight gzinflate + if ( false !== ( $decompressed = @gzinflate( $gzData ) ) ) { + return $decompressed; + } + + // Fallback for all above failing, not expected, but included for + // debugging and preventing regressions and to track stats + if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) ) { + return $decompressed; + } + + return false; + } + + public static function match_domain($host, $reference) { + // Check for a direct match + if ($host === $reference) { + return true; + } + + // Calculate the valid wildcard match if the host is not an IP address + // Also validates that the host has 3 parts or more, as per Firefox's + // ruleset. + $parts = explode('.', $host); + if (ip2long($host) === false && count($parts) >= 3) { + $parts[0] = '*'; + $wildcard = implode('.', $parts); + if ($wildcard === $reference) { + return true; + } + } + + return false; + } +} diff --git a/vendor/rmccue/requests/library/Requests/Auth.php b/vendor/rmccue/requests/library/Requests/Auth.php new file mode 100644 index 00000000000..bca4109209d --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Auth.php @@ -0,0 +1,33 @@ +user, $this->pass) = $args; + } + } + + /** + * Register the necessary callbacks + * + * @see curl_before_send + * @see fsockopen_header + * @param Requests_Hooks $hooks Hook system + */ + public function register(Requests_Hooks &$hooks) { + $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); + $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); + } + + /** + * Set cURL parameters before the data is sent + * + * @param resource $handle cURL resource + */ + public function curl_before_send(&$handle) { + curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString()); + } + + /** + * Add extra headers to the request before sending + * + * @param string $out HTTP header string + */ + public function fsockopen_header(&$out) { + $out .= "Authorization: Basic " . base64_encode($this->getAuthString()) . "\r\n"; + } + + /** + * Get the authentication string (user:pass) + * + * @return string + */ + public function getAuthString() { + return $this->user . ':' . $this->pass; + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Cookie.php b/vendor/rmccue/requests/library/Requests/Cookie.php new file mode 100644 index 00000000000..365fad89362 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Cookie.php @@ -0,0 +1,171 @@ +name = $name; + $this->value = $value; + $this->attributes = $attributes; + } + + /** + * Format a cookie for a Cookie header + * + * This is used when sending cookies to a server. + * + * @return string Cookie formatted for Cookie header + */ + public function formatForHeader() { + return sprintf('%s=%s', $this->name, $this->value); + } + + /** + * Format a cookie for a Set-Cookie header + * + * This is used when sending cookies to clients. This isn't really + * applicable to client-side usage, but might be handy for debugging. + * + * @return string Cookie formatted for Set-Cookie header + */ + public function formatForSetCookie() { + $header_value = $this->formatForHeader(); + if (!empty($this->attributes)) { + $parts = array(); + foreach ($this->attributes as $key => $value) { + // Ignore non-associative attributes + if (is_numeric($key)) { + $parts[] = $value; + } + else { + $parts[] = sprintf('%s=%s', $key, $value); + } + } + + $header_value .= '; ' . implode('; ', $parts); + } + return $header_value; + } + + /** + * Get the cookie value + * + * Attributes and other data can be accessed via methods. + */ + public function __toString() { + return $this->value; + } + + /** + * Parse a cookie string into a cookie object + * + * Based on Mozilla's parsing code in Firefox and related projects, which + * is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265 + * specifies some of this handling, but not in a thorough manner. + * + * @param string Cookie header value (from a Set-Cookie header) + * @return Requests_Cookie Parsed cookie object + */ + public static function parse($string, $name = '') { + $parts = explode(';', $string); + $kvparts = array_shift($parts); + + if (!empty($name)) { + $value = $string; + } + elseif (strpos($kvparts, '=') === false) { + // Some sites might only have a value without the equals separator. + // Deviate from RFC 6265 and pretend it was actually a blank name + // (`=foo`) + // + // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 + $name = ''; + $value = $kvparts; + } + else { + list($name, $value) = explode('=', $kvparts, 2); + } + $name = trim($name); + $value = trim($value); + + // Attribute key are handled case-insensitively + $attributes = new Requests_Utility_CaseInsensitiveDictionary(); + + if (!empty($parts)) { + foreach ($parts as $part) { + if (strpos($part, '=') === false) { + $part_key = $part; + $part_value = true; + } + else { + list($part_key, $part_value) = explode('=', $part, 2); + $part_value = trim($part_value); + } + + $part_key = trim($part_key); + $attributes[$part_key] = $part_value; + } + } + + return new Requests_Cookie($name, $value, $attributes); + } + + /** + * Parse all Set-Cookie headers from request headers + * + * @param Requests_Response_Headers $headers + * @return array + */ + public static function parseFromHeaders(Requests_Response_Headers $headers) { + $cookie_headers = $headers->getValues('Set-Cookie'); + if (empty($cookie_headers)) { + return array(); + } + + $cookies = array(); + foreach ($cookie_headers as $header) { + $parsed = self::parse($header); + $cookies[$parsed->name] = $parsed; + } + + return $cookies; + } +} diff --git a/vendor/rmccue/requests/library/Requests/Cookie/Jar.php b/vendor/rmccue/requests/library/Requests/Cookie/Jar.php new file mode 100644 index 00000000000..6d2f53f5a3c --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Cookie/Jar.php @@ -0,0 +1,146 @@ +cookies = $cookies; + } + + /** + * Normalise cookie data into a Requests_Cookie + * + * @param string|Requests_Cookie $cookie + * @return Requests_Cookie + */ + public function normalizeCookie($cookie, $key = null) { + if ($cookie instanceof Requests_Cookie) { + return $cookie; + } + + return Requests_Cookie::parse($cookie, $key); + } + + /** + * Check if the given item exists + * + * @param string $key Item key + * @return boolean Does the item exist? + */ + public function offsetExists($key) { + return isset($this->cookies[$key]); + } + + /** + * Get the value for the item + * + * @param string $key Item key + * @return string Item value + */ + public function offsetGet($key) { + if (!isset($this->cookies[$key])) + return null; + + return $this->cookies[$key]; + } + + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $this->cookies[$key] = $value; + } + + /** + * Unset the given header + * + * @param string $key + */ + public function offsetUnset($key) { + unset($this->cookies[$key]); + } + + /** + * Get an iterator for the data + * + * @return ArrayIterator + */ + public function getIterator() { + return new ArrayIterator($this->cookies); + } + + /** + * Register the cookie handler with the request's hooking system + * + * @param Requests_Hooker $hooks Hooking system + */ + public function register(Requests_Hooker $hooks) { + $hooks->register('requests.before_request', array($this, 'before_request')); + $hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check')); + } + + /** + * Add Cookie header to a request if we have any + * + * As per RFC 6265, cookies are separated by '; ' + * + * @param string $url + * @param array $headers + * @param array $data + * @param string $type + * @param array $options + */ + public function before_request(&$url, &$headers, &$data, &$type, &$options) { + if (!empty($this->cookies)) { + $cookies = array(); + foreach ($this->cookies as $key => $cookie) { + $cookie = $this->normalizeCookie($cookie, $key); + $cookies[] = $cookie->formatForHeader(); + } + + $headers['Cookie'] = implode('; ', $cookies); + } + } + + /** + * Parse all cookies from a response and attach them to the response + * + * @var Requests_Response $response + */ + public function before_redirect_check(Requests_Response &$return) { + $cookies = Requests_Cookie::parseFromHeaders($return->headers); + $this->cookies = array_merge($this->cookies, $cookies); + $return->cookies = $this; + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Exception.php b/vendor/rmccue/requests/library/Requests/Exception.php new file mode 100644 index 00000000000..37d4711c146 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Exception.php @@ -0,0 +1,62 @@ +type = $type; + $this->data = $data; + } + + /** + * Like {@see getCode()}, but a string code. + * + * @codeCoverageIgnore + * @return string + */ + public function getType() { + return $this->type; + } + + /** + * Gives any relevant data + * + * @codeCoverageIgnore + * @return mixed + */ + public function getData() { + return $this->data; + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Exception/HTTP.php b/vendor/rmccue/requests/library/Requests/Exception/HTTP.php new file mode 100644 index 00000000000..dc8954f1af3 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Exception/HTTP.php @@ -0,0 +1,67 @@ +reason = $reason; + } + + $message = sprintf('%d %s', $this->code, $this->reason); + parent::__construct($message, 'httpresponse', $data, $this->code); + } + + /** + * Get the status message + */ + public function getReason() { + return $this->reason; + } + + /** + * Get the correct exception class for a given error code + * + * @param int $code HTTP status code + * @return string Exception class name to use + */ + public static function get_class($code) { + $class = sprintf('Requests_Exception_HTTP_%d', $code); + if (class_exists($class)) { + return $class; + } + + return 'Requests_Exception_HTTP_Unknown'; + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Exception/HTTP/400.php b/vendor/rmccue/requests/library/Requests/Exception/HTTP/400.php new file mode 100644 index 00000000000..b3ad7743573 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Exception/HTTP/400.php @@ -0,0 +1,27 @@ +code = $data->status_code; + } + + parent::__construct($reason, $data); + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Hooker.php b/vendor/rmccue/requests/library/Requests/Hooker.php new file mode 100644 index 00000000000..f667ae9c58d --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Hooker.php @@ -0,0 +1,33 @@ +0 is executed later + */ + public function register($hook, $callback, $priority = 0); + + /** + * Dispatch a message + * + * @param string $hook Hook name + * @param array $parameters Parameters to pass to callbacks + * @return boolean Successfulness + */ + public function dispatch($hook, $parameters = array()); +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Hooks.php b/vendor/rmccue/requests/library/Requests/Hooks.php new file mode 100644 index 00000000000..7a99b9b1718 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Hooks.php @@ -0,0 +1,61 @@ +0 is executed later + */ + public function register($hook, $callback, $priority = 0) { + if (!isset($this->hooks[$hook])) { + $this->hooks[$hook] = array(); + } + if (!isset($this->hooks[$hook][$priority])) { + $this->hooks[$hook][$priority] = array(); + } + + $this->hooks[$hook][$priority][] = $callback; + } + + /** + * Dispatch a message + * + * @param string $hook Hook name + * @param array $parameters Parameters to pass to callbacks + * @return boolean Successfulness + */ + public function dispatch($hook, $parameters = array()) { + if (empty($this->hooks[$hook])) { + return false; + } + + foreach ($this->hooks[$hook] as $priority => $hooked) { + foreach ($hooked as $callback) { + call_user_func_array($callback, $parameters); + } + } + + return true; + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/IDNAEncoder.php b/vendor/rmccue/requests/library/Requests/IDNAEncoder.php new file mode 100644 index 00000000000..53cdd0a8eb1 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/IDNAEncoder.php @@ -0,0 +1,390 @@ + 0) { + if ($position + $length > $strlen) { + throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); + } + for ($position++; $remaining > 0; $position++) { + $value = ord($input[$position]); + + // If it is invalid, count the sequence as invalid and reprocess the current byte: + if (($value & 0xC0) !== 0x80) { + throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); + } + + $character |= ($value & 0x3F) << (--$remaining * 6); + } + $position--; + } + + if ( + // Non-shortest form sequences are invalid + $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of ucschar codepoints + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + || ( + // Everything else not in ucschar + $character > 0xD7FF && $character < 0xF900 + || $character < 0x20 + || $character > 0x7E && $character < 0xA0 + || $character > 0xEFFFD + ) + ) { + throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); + } + + $codepoints[] = $character; + } + + return $codepoints; + } + + /** + * RFC3492-compliant encoder + * + * @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code + * @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`) + * + * @param string $input UTF-8 encoded string to encode + * @return string Punycode-encoded string + */ + public static function punycode_encode($input) { + $output = ''; +# let n = initial_n + $n = self::BOOTSTRAP_INITIAL_N; +# let delta = 0 + $delta = 0; +# let bias = initial_bias + $bias = self::BOOTSTRAP_INITIAL_BIAS; +# let h = b = the number of basic code points in the input + $h = $b = 0; // see loop +# copy them to the output in order + $codepoints = self::utf8_to_codepoints($input); + + foreach ($codepoints as $char) { + if ($char < 128) { + // Character is valid ASCII + // TODO: this should also check if it's valid for a URL + $output .= chr($char); + $h++; + } + // Check if the character is non-ASCII, but below initial n + // This never occurs for Punycode, so ignore in coverage + // @codeCoverageIgnoreStart + elseif ($char < $n) { + throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char); + } + // @codeCoverageIgnoreEnd + else { + $extended[$char] = true; + } + } + $extended = array_keys($extended); + sort($extended); + $b = $h; +# [copy them] followed by a delimiter if b > 0 + if (strlen($output) > 0) { + $output .= '-'; + } +# {if the input contains a non-basic code point < n then fail} +# while h < length(input) do begin + while ($h < count($codepoints)) { +# let m = the minimum code point >= n in the input + $m = array_shift($extended); + //printf('next code point to insert is %s' . PHP_EOL, dechex($m)); +# let delta = delta + (m - n) * (h + 1), fail on overflow + $delta += ($m - $n) * ($h + 1); +# let n = m + $n = $m; +# for each code point c in the input (in order) do begin + for ($num = 0; $num < count($codepoints); $num++) { + $c = $codepoints[$num]; +# if c < n then increment delta, fail on overflow + if ($c < $n) { + $delta++; + } +# if c == n then begin + elseif ($c === $n) { +# let q = delta + $q = $delta; +# for k = base to infinity in steps of base do begin + for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) { +# let t = tmin if k <= bias {+ tmin}, or +# tmax if k >= bias + tmax, or k - bias otherwise + if ($k <= ($bias + self::BOOTSTRAP_TMIN)) { + $t = self::BOOTSTRAP_TMIN; + } + elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) { + $t = self::BOOTSTRAP_TMAX; + } + else { + $t = $k - $bias; + } +# if q < t then break + if ($q < $t) { + break; + } +# output the code point for digit t + ((q - t) mod (base - t)) + $digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)); + //printf('needed delta is %d, encodes as "%s"' . PHP_EOL, $delta, self::digit_to_char($digit)); + $output .= self::digit_to_char($digit); +# let q = (q - t) div (base - t) + $q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t)); +# end + } +# output the code point for digit q + $output .= self::digit_to_char($q); + //printf('needed delta is %d, encodes as "%s"' . PHP_EOL, $delta, self::digit_to_char($q)); +# let bias = adapt(delta, h + 1, test h equals b?) + $bias = self::adapt($delta, $h + 1, $h === $b); + //printf('bias becomes %d' . PHP_EOL, $bias); +# let delta = 0 + $delta = 0; +# increment h + $h++; +# end + } +# end + } +# increment delta and n + $delta++; + $n++; +# end + } + + return $output; + } + + /** + * Convert a digit to its respective character + * + * @see http://tools.ietf.org/html/rfc3492#section-5 + * @throws Requests_Exception On invalid digit (`idna.invalid_digit`) + * + * @param int $digit Digit in the range 0-35 + * @return string Single character corresponding to digit + */ + protected static function digit_to_char($digit) { + // @codeCoverageIgnoreStart + // As far as I know, this never happens, but still good to be sure. + if ($digit < 0 || $digit > 35) { + throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit); + } + // @codeCoverageIgnoreEnd + $digits = 'abcdefghijklmnopqrstuvwxyz0123456789'; + return substr($digits, $digit, 1); + } + + /** + * Adapt the bias + * + * @see http://tools.ietf.org/html/rfc3492#section-6.1 + * @param int $delta + * @param int $numpoints + * @param bool $firsttime + * @return int New bias + */ + protected static function adapt($delta, $numpoints, $firsttime) { +# function adapt(delta,numpoints,firsttime): +# if firsttime then let delta = delta div damp + if ($firsttime) { + $delta = floor($delta / self::BOOTSTRAP_DAMP); + } +# else let delta = delta div 2 + else { + $delta = floor($delta / 2); + } +# let delta = delta + (delta div numpoints) + $delta += floor($delta / $numpoints); +# let k = 0 + $k = 0; +# while delta > ((base - tmin) * tmax) div 2 do begin + $max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2); + while ($delta > $max) { +# let delta = delta div (base - tmin) + $delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN)); +# let k = k + base + $k += self::BOOTSTRAP_BASE; +# end + } +# return k + (((base - tmin + 1) * delta) div (delta + skew)) + return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW)); + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/IPv6.php b/vendor/rmccue/requests/library/Requests/IPv6.php new file mode 100644 index 00000000000..5e5c259e555 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/IPv6.php @@ -0,0 +1,221 @@ + FF01:0:0:0:0:0:0:101 + * ::1 -> 0:0:0:0:0:0:0:1 + * + * @author Alexander Merz + * @author elfrink at introweb dot nl + * @author Josh Peck + * @copyright 2003-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php + * @param string $ip An IPv6 address + * @return string The uncompressed IPv6 address + */ + public static function uncompress($ip) + { + $c1 = -1; + $c2 = -1; + if (substr_count($ip, '::') === 1) + { + list($ip1, $ip2) = explode('::', $ip); + if ($ip1 === '') + { + $c1 = -1; + } + else + { + $c1 = substr_count($ip1, ':'); + } + if ($ip2 === '') + { + $c2 = -1; + } + else + { + $c2 = substr_count($ip2, ':'); + } + if (strpos($ip2, '.') !== false) + { + $c2++; + } + // :: + if ($c1 === -1 && $c2 === -1) + { + $ip = '0:0:0:0:0:0:0:0'; + } + // ::xxx + else if ($c1 === -1) + { + $fill = str_repeat('0:', 7 - $c2); + $ip = str_replace('::', $fill, $ip); + } + // xxx:: + else if ($c2 === -1) + { + $fill = str_repeat(':0', 7 - $c1); + $ip = str_replace('::', $fill, $ip); + } + // xxx::xxx + else + { + $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); + $ip = str_replace('::', $fill, $ip); + } + } + return $ip; + } + + /** + * Compresses an IPv6 address + * + * RFC 4291 allows you to compress consecutive zero pieces in an address to + * '::'. This method expects a valid IPv6 address and compresses consecutive + * zero pieces to '::'. + * + * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 + * 0:0:0:0:0:0:0:1 -> ::1 + * + * @see uncompress() + * @param string $ip An IPv6 address + * @return string The compressed IPv6 address + */ + public static function compress($ip) + { + // Prepare the IP to be compressed + $ip = self::uncompress($ip); + $ip_parts = self::split_v6_v4($ip); + + // Replace all leading zeros + $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); + + // Find bunches of zeros + if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) + { + $max = 0; + $pos = null; + foreach ($matches[0] as $match) + { + if (strlen($match[0]) > $max) + { + $max = strlen($match[0]); + $pos = $match[1]; + } + } + + $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); + } + + if ($ip_parts[1] !== '') + { + return implode(':', $ip_parts); + } + else + { + return $ip_parts[0]; + } + } + + /** + * Splits an IPv6 address into the IPv6 and IPv4 representation parts + * + * RFC 4291 allows you to represent the last two parts of an IPv6 address + * using the standard IPv4 representation + * + * Example: 0:0:0:0:0:0:13.1.68.3 + * 0:0:0:0:0:FFFF:129.144.52.38 + * + * @param string $ip An IPv6 address + * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part + */ + private static function split_v6_v4($ip) + { + if (strpos($ip, '.') !== false) + { + $pos = strrpos($ip, ':'); + $ipv6_part = substr($ip, 0, $pos); + $ipv4_part = substr($ip, $pos + 1); + return array($ipv6_part, $ipv4_part); + } + else + { + return array($ip, ''); + } + } + + /** + * Checks an IPv6 address + * + * Checks if the given IP is a valid IPv6 address + * + * @param string $ip An IPv6 address + * @return bool true if $ip is a valid IPv6 address + */ + public static function check_ipv6($ip) + { + $ip = self::uncompress($ip); + list($ipv6, $ipv4) = self::split_v6_v4($ip); + $ipv6 = explode(':', $ipv6); + $ipv4 = explode('.', $ipv4); + if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) + { + foreach ($ipv6 as $ipv6_part) + { + // The section can't be empty + if ($ipv6_part === '') + return false; + + // Nor can it be over four characters + if (strlen($ipv6_part) > 4) + return false; + + // Remove leading zeros (this is safe because of the above) + $ipv6_part = ltrim($ipv6_part, '0'); + if ($ipv6_part === '') + $ipv6_part = '0'; + + // Check the value is valid + $value = hexdec($ipv6_part); + if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) + return false; + } + if (count($ipv4) === 4) + { + foreach ($ipv4 as $ipv4_part) + { + $value = (int) $ipv4_part; + if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) + return false; + } + } + return true; + } + else + { + return false; + } + } +} diff --git a/vendor/rmccue/requests/library/Requests/IRI.php b/vendor/rmccue/requests/library/Requests/IRI.php new file mode 100644 index 00000000000..b8dceae4337 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/IRI.php @@ -0,0 +1,1220 @@ + array( + 'port' => 674 + ), + 'dict' => array( + 'port' => 2628 + ), + 'file' => array( + 'ihost' => 'localhost' + ), + 'http' => array( + 'port' => 80, + 'ipath' => '/' + ), + 'https' => array( + 'port' => 443, + 'ipath' => '/' + ), + ); + + /** + * Return the entire IRI when you try and read the object as a string + * + * @return string + */ + public function __toString() + { + return $this->get_iri(); + } + + /** + * Overload __set() to provide access via properties + * + * @param string $name Property name + * @param mixed $value Property value + */ + public function __set($name, $value) + { + if (method_exists($this, 'set_' . $name)) + { + call_user_func(array($this, 'set_' . $name), $value); + } + elseif ( + $name === 'iauthority' + || $name === 'iuserinfo' + || $name === 'ihost' + || $name === 'ipath' + || $name === 'iquery' + || $name === 'ifragment' + ) + { + call_user_func(array($this, 'set_' . substr($name, 1)), $value); + } + } + + /** + * Overload __get() to provide access via properties + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + // isset() returns false for null, we don't want to do that + // Also why we use array_key_exists below instead of isset() + $props = get_object_vars($this); + + if ( + $name === 'iri' || + $name === 'uri' || + $name === 'iauthority' || + $name === 'authority' + ) + { + $return = $this->{"get_$name"}(); + } + elseif (array_key_exists($name, $props)) + { + $return = $this->$name; + } + // host -> ihost + elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) + { + $name = $prop; + $return = $this->$prop; + } + // ischeme -> scheme + elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) + { + $name = $prop; + $return = $this->$prop; + } + else + { + trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); + $return = null; + } + + if ($return === null && isset($this->normalization[$this->scheme][$name])) + { + return $this->normalization[$this->scheme][$name]; + } + else + { + return $return; + } + } + + /** + * Overload __isset() to provide access via properties + * + * @param string $name Property name + * @return bool + */ + public function __isset($name) + { + if (method_exists($this, 'get_' . $name) || isset($this->$name)) + { + return true; + } + else + { + return false; + } + } + + /** + * Overload __unset() to provide access via properties + * + * @param string $name Property name + */ + public function __unset($name) + { + if (method_exists($this, 'set_' . $name)) + { + call_user_func(array($this, 'set_' . $name), ''); + } + } + + /** + * Create a new IRI object, from a specified string + * + * @param string $iri + */ + public function __construct($iri = null) + { + $this->set_iri($iri); + } + + /** + * Create a new IRI object by resolving a relative IRI + * + * Returns false if $base is not absolute, otherwise an IRI. + * + * @param IRI|string $base (Absolute) Base IRI + * @param IRI|string $relative Relative IRI + * @return IRI|false + */ + public static function absolutize($base, $relative) + { + if (!($relative instanceof Requests_IRI)) + { + $relative = new Requests_IRI($relative); + } + if (!$relative->is_valid()) + { + return false; + } + elseif ($relative->scheme !== null) + { + return clone $relative; + } + else + { + if (!($base instanceof Requests_IRI)) + { + $base = new Requests_IRI($base); + } + if ($base->scheme !== null && $base->is_valid()) + { + if ($relative->get_iri() !== '') + { + if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) + { + $target = clone $relative; + $target->scheme = $base->scheme; + } + else + { + $target = new Requests_IRI; + $target->scheme = $base->scheme; + $target->iuserinfo = $base->iuserinfo; + $target->ihost = $base->ihost; + $target->port = $base->port; + if ($relative->ipath !== '') + { + if ($relative->ipath[0] === '/') + { + $target->ipath = $relative->ipath; + } + elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') + { + $target->ipath = '/' . $relative->ipath; + } + elseif (($last_segment = strrpos($base->ipath, '/')) !== false) + { + $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; + } + else + { + $target->ipath = $relative->ipath; + } + $target->ipath = $target->remove_dot_segments($target->ipath); + $target->iquery = $relative->iquery; + } + else + { + $target->ipath = $base->ipath; + if ($relative->iquery !== null) + { + $target->iquery = $relative->iquery; + } + elseif ($base->iquery !== null) + { + $target->iquery = $base->iquery; + } + } + $target->ifragment = $relative->ifragment; + } + } + else + { + $target = clone $base; + $target->ifragment = null; + } + $target->scheme_normalization(); + return $target; + } + else + { + return false; + } + } + } + + /** + * Parse an IRI into scheme/authority/path/query/fragment segments + * + * @param string $iri + * @return array + */ + protected function parse_iri($iri) + { + $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); + if (preg_match('/^((?P[^:\/?#]+):)?(\/\/(?P[^\/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?$/', $iri, $match)) + { + if ($match[1] === '') + { + $match['scheme'] = null; + } + if (!isset($match[3]) || $match[3] === '') + { + $match['authority'] = null; + } + if (!isset($match[5])) + { + $match['path'] = ''; + } + if (!isset($match[6]) || $match[6] === '') + { + $match['query'] = null; + } + if (!isset($match[8]) || $match[8] === '') + { + $match['fragment'] = null; + } + return $match; + } + else + { + trigger_error('This should never happen', E_USER_ERROR); + die; + } + } + + /** + * Remove dot segments from a path + * + * @param string $input + * @return string + */ + protected function remove_dot_segments($input) + { + $output = ''; + while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') + { + // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, + if (strpos($input, '../') === 0) + { + $input = substr($input, 3); + } + elseif (strpos($input, './') === 0) + { + $input = substr($input, 2); + } + // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, + elseif (strpos($input, '/./') === 0) + { + $input = substr($input, 2); + } + elseif ($input === '/.') + { + $input = '/'; + } + // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, + elseif (strpos($input, '/../') === 0) + { + $input = substr($input, 3); + $output = substr_replace($output, '', strrpos($output, '/')); + } + elseif ($input === '/..') + { + $input = '/'; + $output = substr_replace($output, '', strrpos($output, '/')); + } + // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, + elseif ($input === '.' || $input === '..') + { + $input = ''; + } + // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer + elseif (($pos = strpos($input, '/', 1)) !== false) + { + $output .= substr($input, 0, $pos); + $input = substr_replace($input, '', 0, $pos); + } + else + { + $output .= $input; + $input = ''; + } + } + return $output . $input; + } + + /** + * Replace invalid character with percent encoding + * + * @param string $string Input string + * @param string $extra_chars Valid characters not in iunreserved or + * iprivate (this is ASCII-only) + * @param bool $iprivate Allow iprivate + * @return string + */ + protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) + { + // Normalize as many pct-encoded sections as possible + $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string); + + // Replace invalid percent characters + $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); + + // Add unreserved and % to $extra_chars (the latter is safe because all + // pct-encoded sections are now valid). + $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; + + // Now replace any bytes that aren't allowed with their pct-encoded versions + $position = 0; + $strlen = strlen($string); + while (($position += strspn($string, $extra_chars, $position)) < $strlen) + { + $value = ord($string[$position]); + + // Start position + $start = $position; + + // By default we are valid + $valid = true; + + // No one byte sequences are valid due to the while. + // Two byte sequence: + if (($value & 0xE0) === 0xC0) + { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) + { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) + { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else + { + $valid = false; + $length = 1; + $remaining = 0; + } + + if ($remaining) + { + if ($position + $length <= $strlen) + { + for ($position++; $remaining; $position++) + { + $value = ord($string[$position]); + + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) + { + $character |= ($value & 0x3F) << (--$remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte: + else + { + $valid = false; + $position--; + break; + } + } + } + else + { + $position = $strlen - 1; + $valid = false; + } + } + + // Percent encode anything invalid or not in ucschar + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of ucschar codepoints + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + || ( + // Everything else not in ucschar + $character > 0xD7FF && $character < 0xF900 + || $character < 0xA0 + || $character > 0xEFFFD + ) + && ( + // Everything not in iprivate, if it applies + !$iprivate + || $character < 0xE000 + || $character > 0x10FFFD + ) + ) + { + // If we were a character, pretend we weren't, but rather an error. + if ($valid) + $position--; + + for ($j = $start; $j <= $position; $j++) + { + $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); + $j += 2; + $position += 2; + $strlen += 2; + } + } + } + + return $string; + } + + /** + * Callback function for preg_replace_callback. + * + * Removes sequences of percent encoded bytes that represent UTF-8 + * encoded characters in iunreserved + * + * @param array $match PCRE match + * @return string Replacement + */ + protected function remove_iunreserved_percent_encoded($match) + { + // As we just have valid percent encoded sequences we can just explode + // and ignore the first member of the returned array (an empty string). + $bytes = explode('%', $match[0]); + + // Initialize the new string (this is what will be returned) and that + // there are no bytes remaining in the current sequence (unsurprising + // at the first byte!). + $string = ''; + $remaining = 0; + + // Loop over each and every byte, and set $value to its value + for ($i = 1, $len = count($bytes); $i < $len; $i++) + { + $value = hexdec($bytes[$i]); + + // If we're the first byte of sequence: + if (!$remaining) + { + // Start position + $start = $i; + + // By default we are valid + $valid = true; + + // One byte sequence: + if ($value <= 0x7F) + { + $character = $value; + $length = 1; + } + // Two byte sequence: + elseif (($value & 0xE0) === 0xC0) + { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) + { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) + { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else + { + $valid = false; + $remaining = 0; + } + } + // Continuation byte: + else + { + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) + { + $remaining--; + $character |= ($value & 0x3F) << ($remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: + else + { + $valid = false; + $remaining = 0; + $i--; + } + } + + // If we've reached the end of the current byte sequence, append it to Unicode::$data + if (!$remaining) + { + // Percent encode anything invalid or not in iunreserved + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of iunreserved codepoints + || $character < 0x2D + || $character > 0xEFFFD + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + // Everything else not in iunreserved (this is all BMP) + || $character === 0x2F + || $character > 0x39 && $character < 0x41 + || $character > 0x5A && $character < 0x61 + || $character > 0x7A && $character < 0x7E + || $character > 0x7E && $character < 0xA0 + || $character > 0xD7FF && $character < 0xF900 + ) + { + for ($j = $start; $j <= $i; $j++) + { + $string .= '%' . strtoupper($bytes[$j]); + } + } + else + { + for ($j = $start; $j <= $i; $j++) + { + $string .= chr(hexdec($bytes[$j])); + } + } + } + } + + // If we have any bytes left over they are invalid (i.e., we are + // mid-way through a multi-byte sequence) + if ($remaining) + { + for ($j = $start; $j < $len; $j++) + { + $string .= '%' . strtoupper($bytes[$j]); + } + } + + return $string; + } + + protected function scheme_normalization() + { + if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) + { + $this->iuserinfo = null; + } + if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) + { + $this->ihost = null; + } + if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) + { + $this->port = null; + } + if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) + { + $this->ipath = ''; + } + if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) + { + $this->iquery = null; + } + if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) + { + $this->ifragment = null; + } + } + + /** + * Check if the object represents a valid IRI. This needs to be done on each + * call as some things change depending on another part of the IRI. + * + * @return bool + */ + public function is_valid() + { + $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; + if ($this->ipath !== '' && + ( + $isauthority && ( + $this->ipath[0] !== '/' || + substr($this->ipath, 0, 2) === '//' + ) || + ( + $this->scheme === null && + !$isauthority && + strpos($this->ipath, ':') !== false && + (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) + ) + ) + ) + { + return false; + } + + return true; + } + + /** + * Set the entire IRI. Returns true on success, false on failure (if there + * are any invalid characters). + * + * @param string $iri + * @return bool + */ + protected function set_iri($iri) + { + static $cache; + if (!$cache) + { + $cache = array(); + } + + if ($iri === null) + { + return true; + } + elseif (isset($cache[$iri])) + { + list($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return) = $cache[$iri]; + return $return; + } + else + { + $parsed = $this->parse_iri((string) $iri); + + $return = $this->set_scheme($parsed['scheme']) + && $this->set_authority($parsed['authority']) + && $this->set_path($parsed['path']) + && $this->set_query($parsed['query']) + && $this->set_fragment($parsed['fragment']); + + $cache[$iri] = array($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return); + return $return; + } + } + + /** + * Set the scheme. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $scheme + * @return bool + */ + protected function set_scheme($scheme) + { + if ($scheme === null) + { + $this->scheme = null; + } + elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) + { + $this->scheme = null; + return false; + } + else + { + $this->scheme = strtolower($scheme); + } + return true; + } + + /** + * Set the authority. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $authority + * @return bool + */ + protected function set_authority($authority) + { + static $cache; + if (!$cache) + $cache = array(); + + if ($authority === null) + { + $this->iuserinfo = null; + $this->ihost = null; + $this->port = null; + return true; + } + elseif (isset($cache[$authority])) + { + list($this->iuserinfo, + $this->ihost, + $this->port, + $return) = $cache[$authority]; + + return $return; + } + else + { + $remaining = $authority; + if (($iuserinfo_end = strrpos($remaining, '@')) !== false) + { + $iuserinfo = substr($remaining, 0, $iuserinfo_end); + $remaining = substr($remaining, $iuserinfo_end + 1); + } + else + { + $iuserinfo = null; + } + if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) + { + if (($port = substr($remaining, $port_start + 1)) === false) + { + $port = null; + } + $remaining = substr($remaining, 0, $port_start); + } + else + { + $port = null; + } + + $return = $this->set_userinfo($iuserinfo) && + $this->set_host($remaining) && + $this->set_port($port); + + $cache[$authority] = array($this->iuserinfo, + $this->ihost, + $this->port, + $return); + + return $return; + } + } + + /** + * Set the iuserinfo. + * + * @param string $iuserinfo + * @return bool + */ + protected function set_userinfo($iuserinfo) + { + if ($iuserinfo === null) + { + $this->iuserinfo = null; + } + else + { + $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); + $this->scheme_normalization(); + } + + return true; + } + + /** + * Set the ihost. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $ihost + * @return bool + */ + protected function set_host($ihost) + { + if ($ihost === null) + { + $this->ihost = null; + return true; + } + elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') + { + if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) + { + $this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']'; + } + else + { + $this->ihost = null; + return false; + } + } + else + { + $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); + + // Lowercase, but ignore pct-encoded sections (as they should + // remain uppercase). This must be done after the previous step + // as that can add unescaped characters. + $position = 0; + $strlen = strlen($ihost); + while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) + { + if ($ihost[$position] === '%') + { + $position += 3; + } + else + { + $ihost[$position] = strtolower($ihost[$position]); + $position++; + } + } + + $this->ihost = $ihost; + } + + $this->scheme_normalization(); + + return true; + } + + /** + * Set the port. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $port + * @return bool + */ + protected function set_port($port) + { + if ($port === null) + { + $this->port = null; + return true; + } + elseif (strspn($port, '0123456789') === strlen($port)) + { + $this->port = (int) $port; + $this->scheme_normalization(); + return true; + } + else + { + $this->port = null; + return false; + } + } + + /** + * Set the ipath. + * + * @param string $ipath + * @return bool + */ + protected function set_path($ipath) + { + static $cache; + if (!$cache) + { + $cache = array(); + } + + $ipath = (string) $ipath; + + if (isset($cache[$ipath])) + { + $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; + } + else + { + $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); + $removed = $this->remove_dot_segments($valid); + + $cache[$ipath] = array($valid, $removed); + $this->ipath = ($this->scheme !== null) ? $removed : $valid; + } + + $this->scheme_normalization(); + return true; + } + + /** + * Set the iquery. + * + * @param string $iquery + * @return bool + */ + protected function set_query($iquery) + { + if ($iquery === null) + { + $this->iquery = null; + } + else + { + $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); + $this->scheme_normalization(); + } + return true; + } + + /** + * Set the ifragment. + * + * @param string $ifragment + * @return bool + */ + protected function set_fragment($ifragment) + { + if ($ifragment === null) + { + $this->ifragment = null; + } + else + { + $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); + $this->scheme_normalization(); + } + return true; + } + + /** + * Convert an IRI to a URI (or parts thereof) + * + * @return string + */ + protected function to_uri($string) + { + static $non_ascii; + if (!$non_ascii) + { + $non_ascii = implode('', range("\x80", "\xFF")); + } + + $position = 0; + $strlen = strlen($string); + while (($position += strcspn($string, $non_ascii, $position)) < $strlen) + { + $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); + $position += 3; + $strlen += 2; + } + + return $string; + } + + /** + * Get the complete IRI + * + * @return string + */ + protected function get_iri() + { + if (!$this->is_valid()) + { + return false; + } + + $iri = ''; + if ($this->scheme !== null) + { + $iri .= $this->scheme . ':'; + } + if (($iauthority = $this->get_iauthority()) !== null) + { + $iri .= '//' . $iauthority; + } + $iri .= $this->ipath; + if ($this->iquery !== null) + { + $iri .= '?' . $this->iquery; + } + if ($this->ifragment !== null) + { + $iri .= '#' . $this->ifragment; + } + + return $iri; + } + + /** + * Get the complete URI + * + * @return string + */ + protected function get_uri() + { + return $this->to_uri($this->get_iri()); + } + + /** + * Get the complete iauthority + * + * @return string + */ + protected function get_iauthority() + { + if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null) + { + $iauthority = ''; + if ($this->iuserinfo !== null) + { + $iauthority .= $this->iuserinfo . '@'; + } + if ($this->ihost !== null) + { + $iauthority .= $this->ihost; + } + if ($this->port !== null) + { + $iauthority .= ':' . $this->port; + } + return $iauthority; + } + else + { + return null; + } + } + + /** + * Get the complete authority + * + * @return string + */ + protected function get_authority() + { + $iauthority = $this->get_iauthority(); + if (is_string($iauthority)) + return $this->to_uri($iauthority); + else + return $iauthority; + } +} diff --git a/vendor/rmccue/requests/library/Requests/Proxy.php b/vendor/rmccue/requests/library/Requests/Proxy.php new file mode 100644 index 00000000000..ac7c1d6b08e --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Proxy.php @@ -0,0 +1,35 @@ +proxy = $args; + } + elseif (is_array($args)) { + if (count($args) == 1) { + list($this->proxy) = $args; + } + elseif (count($args) == 3) { + list($this->proxy, $this->user, $this->pass) = $args; + $this->use_authentication = true; + } + else { + throw new Requests_Exception( 'Invalid number of arguments', 'proxyhttpbadargs'); + } + } + } + + /** + * Register the necessary callbacks + * + * @since 1.6 + * @see curl_before_send + * @see fsockopen_remote_socket + * @see fsockopen_remote_host_path + * @see fsockopen_header + * @param Requests_Hooks $hooks Hook system + */ + public function register(Requests_Hooks &$hooks) { + $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); + + $hooks->register('fsockopen.remote_socket', array(&$this, 'fsockopen_remote_socket')); + $hooks->register('fsockopen.remote_host_path', array(&$this, 'fsockopen_remote_host_path')); + if( $this->use_authentication ) { + $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); + } + } + + /** + * Set cURL parameters before the data is sent + * + * @since 1.6 + * @param resource $handle cURL resource + */ + public function curl_before_send(&$handle) { + curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + curl_setopt($handle, CURLOPT_PROXY, $this->proxy); + + if ($this->use_authentication) { + curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string()); + } + } + + /** + * Alter remote socket information before opening socket connection + * + * @since 1.6 + * @param string $out HTTP header string + */ + public function fsockopen_remote_socket( &$remote_socket ) { + $remote_socket = $this->proxy; + } + + /** + * Alter remote path before getting stream data + * + * @since 1.6 + * @param string $out HTTP header string + */ + public function fsockopen_remote_host_path( &$path, $url ) { + $path = $url; + } + + /** + * Add extra headers to the request before sending + * + * @since 1.6 + * @param string $out HTTP header string + */ + public function fsockopen_header( &$out ) { + $out .= "Proxy-Authorization: Basic " . base64_encode($this->get_auth_string()) . "\r\n"; + } + + /** + * Get the authentication string (user:pass) + * + * @since 1.6 + * @return string + */ + public function get_auth_string() { + return $this->user . ':' . $this->pass; + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Response.php b/vendor/rmccue/requests/library/Requests/Response.php new file mode 100644 index 00000000000..684d2d6c316 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Response.php @@ -0,0 +1,95 @@ +headers = new Requests_Response_Headers(); + } + + /** + * Response body + * @var string + */ + public $body = ''; + + /** + * Raw HTTP data from the transport + * @var string + */ + public $raw = ''; + + /** + * Headers, as an associative array + * @var array + */ + public $headers = array(); + + /** + * Status code, false if non-blocking + * @var integer|boolean + */ + public $status_code = false; + + /** + * Whether the request succeeded or not + * @var boolean + */ + public $success = false; + + /** + * Number of redirects the request used + * @var integer + */ + public $redirects = 0; + + /** + * URL requested + * @var string + */ + public $url = ''; + + /** + * Previous requests (from redirects) + * @var array Array of Requests_Response objects + */ + public $history = array(); + + /** + * Cookies from the request + */ + public $cookies = array(); + + /** + * Throws an exception if the request was not successful + * + * @throws Requests_Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`) + * @throws Requests_Exception_HTTP On non-successful status code. Exception class corresponds to code (e.g. {@see Requests_Exception_HTTP_404}) + * @param boolean $allow_redirects Set to false to throw on a 3xx as well + */ + public function throw_for_status($allow_redirects = true) { + if ($this->status_code >= 300 && $this->status_code < 400) { + if (!$allow_redirects) { + throw new Requests_Exception('Redirection not allowed', 'response.no_redirects', $this); + } + } + + elseif (!$this->success) { + $exception = Requests_Exception_HTTP::get_class($this->status_code); + throw new $exception(null, $this); + } + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Response/Headers.php b/vendor/rmccue/requests/library/Requests/Response/Headers.php new file mode 100644 index 00000000000..aa907256053 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Response/Headers.php @@ -0,0 +1,95 @@ +data[$key])) + return null; + + return $this->flatten($this->data[$key]); + } + + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $key = strtolower($key); + + if (!isset($this->data[$key])) { + $this->data[$key] = array(); + } + + $this->data[$key][] = $value; + } + + /** + * Get all values for a given header + * + * @param string $key + * @return array Header values + */ + public function getValues($key) { + $key = strtolower($key); + if (!isset($this->data[$key])) + return null; + + return $this->data[$key]; + } + + /** + * Flattens a value into a string + * + * Converts an array into a string by imploding values with a comma, as per + * RFC2616's rules for folding headers. + * + * @param string|array $value Value to flatten + * @return string Flattened value + */ + public function flatten($value) { + if (is_array($value)) + $value = implode(',', $value); + + return $value; + } + + /** + * Get an iterator for the data + * + * Converts the internal + * @return ArrayIterator + */ + public function getIterator() { + return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten')); + } +} diff --git a/vendor/rmccue/requests/library/Requests/SSL.php b/vendor/rmccue/requests/library/Requests/SSL.php new file mode 100644 index 00000000000..1ddd894ab95 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/SSL.php @@ -0,0 +1,151 @@ +useragent = 'X';` + * + * @var array + */ + public $options = array(); + + /** + * Create a new session + * + * @param string|null $url Base URL for requests + * @param array $headers Default headers for requests + * @param array $data Default data for requests + * @param array $options Default options for requests + */ + public function __construct($url = null, $headers = array(), $data = array(), $options = array()) { + $this->url = $url; + $this->headers = $headers; + $this->data = $data; + $this->options = $options; + + if (empty($this->options['cookies'])) { + $this->options['cookies'] = new Requests_Cookie_Jar(); + } + } + + /** + * Get a property's value + * + * @param string $key Property key + * @return mixed|null Property value, null if none found + */ + public function __get($key) { + if (isset($this->options[$key])) + return $this->options[$key]; + + return null; + } + + /** + * Set a property's value + * + * @param string $key Property key + * @param mixed $value Property value + */ + public function __set($key, $value) { + $this->options[$key] = $value; + } + + /** + * Remove a property's value + * + * @param string $key Property key + */ + public function __isset($key) { + return isset($this->options[$key]); + } + + /** + * Remove a property's value + * + * @param string $key Property key + */ + public function __unset($key) { + $this->options[$key] = null; + } + + /**#@+ + * @see request() + * @param string $url + * @param array $headers + * @param array $options + * @return Requests_Response + */ + /** + * Send a GET request + */ + public function get($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::GET, $options); + } + + /** + * Send a HEAD request + */ + public function head($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::HEAD, $options); + } + + /** + * Send a DELETE request + */ + public function delete($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::DELETE, $options); + } + /**#@-*/ + + /**#@+ + * @see request() + * @param string $url + * @param array $headers + * @param array $data + * @param array $options + * @return Requests_Response + */ + /** + * Send a POST request + */ + public function post($url, $headers = array(), $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::POST, $options); + } + + /** + * Send a PUT request + */ + public function put($url, $headers = array(), $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::PUT, $options); + } + + /** + * Send a PATCH request + * + * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the + * specification recommends that should send an ETag + * + * @link http://tools.ietf.org/html/rfc5789 + */ + public function patch($url, $headers, $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::PATCH, $options); + } + /**#@-*/ + + /** + * Main interface for HTTP requests + * + * This method initiates a request and sends it via a transport before + * parsing. + * + * @see Requests::request() + * + * @throws Requests_Exception On invalid URLs (`nonhttp`) + * + * @param string $url URL to request + * @param array $headers Extra headers to send with the request + * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests + * @param string $type HTTP request type (use Requests constants) + * @param array $options Options for the request (see {@see Requests::request}) + * @return Requests_Response + */ + public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) { + $request = $this->merge_request(compact('url', 'headers', 'data', 'options')); + + return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']); + } + + /** + * Send multiple HTTP requests simultaneously + * + * @see Requests::request_multiple() + * + * @param array $requests Requests data (see {@see Requests::request_multiple}) + * @param array $options Global and default options (see {@see Requests::request}) + * @return array Responses (either Requests_Response or a Requests_Exception object) + */ + public function request_multiple($requests, $options = array()) { + foreach ($requests as $key => $request) { + $requests[$key] = $this->merge_request($request, false); + } + + $options = array_merge($this->options, $options); + + // Disallow forcing the type, as that's a per request setting + unset($options['type']); + + return Requests::request_multiple($requests, $options); + } + + /** + * Merge a request's data with the default data + * + * @param array $request Request data (same form as {@see request_multiple}) + * @param boolean $merge_options Should we merge options as well? + * @return array Request data + */ + protected function merge_request($request, $merge_options = true) { + if ($this->url !== null) { + $request['url'] = Requests_IRI::absolutize($this->url, $request['url']); + $request['url'] = $request['url']->uri; + } + $request['headers'] = array_merge($this->headers, $request['headers']); + + if (is_array($request['data']) && is_array($this->data)) { + $request['data'] = array_merge($this->data, $request['data']); + } + + if ($merge_options !== false) { + $request['options'] = array_merge($this->options, $request['options']); + + // Disallow forcing the type, as that's a per request setting + unset($request['options']['type']); + } + return $request; + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/library/Requests/Transport.php b/vendor/rmccue/requests/library/Requests/Transport.php new file mode 100644 index 00000000000..7e4a26293a2 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Transport.php @@ -0,0 +1,41 @@ +version = $curl['version']; + $this->fp = curl_init(); + + curl_setopt($this->fp, CURLOPT_HEADER, false); + curl_setopt($this->fp, CURLOPT_RETURNTRANSFER, 1); + if (version_compare($this->version, '7.10.5', '>=')) { + curl_setopt($this->fp, CURLOPT_ENCODING, ''); + } + if (defined('CURLOPT_PROTOCOLS')) { + curl_setopt($this->fp, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + } + if (defined('CURLOPT_REDIR_PROTOCOLS')) { + curl_setopt($this->fp, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + } + } + + /** + * Perform a request + * + * @throws Requests_Exception On a cURL error (`curlerror`) + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + * @return string Raw HTTP result + */ + public function request($url, $headers = array(), $data = array(), $options = array()) { + $this->setup_handle($url, $headers, $data, $options); + + $options['hooks']->dispatch('curl.before_send', array(&$this->fp)); + + if ($options['filename'] !== false) { + $this->stream_handle = fopen($options['filename'], 'wb'); + curl_setopt($this->fp, CURLOPT_FILE, $this->stream_handle); + } + + if (isset($options['verify'])) { + if ($options['verify'] === false) { + curl_setopt($this->fp, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($this->fp, CURLOPT_SSL_VERIFYPEER, 0); + + } elseif (is_string($options['verify'])) { + curl_setopt($this->fp, CURLOPT_CAINFO, $options['verify']); + } + } + + if (isset($options['verifyname']) && $options['verifyname'] === false) { + curl_setopt($this->fp, CURLOPT_SSL_VERIFYHOST, 0); + } + + $response = curl_exec($this->fp); + + $options['hooks']->dispatch('curl.after_send', array(&$fake_headers)); + + if (curl_errno($this->fp) === 23 || curl_errno($this->fp) === 61) { + curl_setopt($this->fp, CURLOPT_ENCODING, 'none'); + $response = curl_exec($this->fp); + } + + $this->process_response($response, $options); + curl_close($this->fp); + return $this->headers; + } + + /** + * Send multiple requests simultaneously + * + * @param array $requests Request data + * @param array $options Global options + * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) + */ + public function request_multiple($requests, $options) { + $multihandle = curl_multi_init(); + $subrequests = array(); + $subhandles = array(); + + $class = get_class($this); + foreach ($requests as $id => $request) { + $subrequests[$id] = new $class(); + $subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']); + $request['options']['hooks']->dispatch('curl.before_multi_add', array(&$subhandles[$id])); + curl_multi_add_handle($multihandle, $subhandles[$id]); + } + + $completed = 0; + $responses = array(); + + $request['options']['hooks']->dispatch('curl.before_multi_exec', array(&$multihandle)); + + do { + $active = false; + + do { + $status = curl_multi_exec($multihandle, $active); + } + while ($status === CURLM_CALL_MULTI_PERFORM); + + $to_process = array(); + + // Read the information as needed + while ($done = curl_multi_info_read($multihandle)) { + $key = array_search($done['handle'], $subhandles, true); + if (!isset($to_process[$key])) { + $to_process[$key] = $done; + } + } + + // Parse the finished requests before we start getting the new ones + foreach ($to_process as $key => $done) { + $options = $requests[$key]['options']; + $responses[$key] = $subrequests[$key]->process_response(curl_multi_getcontent($done['handle']), $options); + + $options['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$key], $requests[$key])); + + curl_multi_remove_handle($multihandle, $done['handle']); + curl_close($done['handle']); + + if (!is_string($responses[$key])) { + $options['hooks']->dispatch('multiple.request.complete', array(&$responses[$key], $key)); + } + $completed++; + } + } + while ($active || $completed < count($subrequests)); + + $request['options']['hooks']->dispatch('curl.after_multi_exec', array(&$multihandle)); + + curl_multi_close($multihandle); + + return $responses; + } + + /** + * Get the cURL handle for use in a multi-request + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + * @return resource Subrequest's cURL handle + */ + public function &get_subrequest_handle($url, $headers, $data, $options) { + $this->setup_handle($url, $headers, $data, $options); + + if ($options['filename'] !== false) { + $this->stream_handle = fopen($options['filename'], 'wb'); + curl_setopt($this->fp, CURLOPT_FILE, $this->stream_handle); + } + + return $this->fp; + } + + /** + * Setup the cURL handle for the given data + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + */ + protected function setup_handle($url, $headers, $data, $options) { + $options['hooks']->dispatch('curl.before_request', array(&$this->fp)); + + $headers = Requests::flatten($headers); + if (in_array($options['type'], array(Requests::HEAD, Requests::GET, Requests::DELETE)) & !empty($data)) { + $url = self::format_get($url, $data); + } + elseif (!empty($data) && !is_string($data)) { + $data = http_build_query($data, null, '&'); + } + + switch ($options['type']) { + case Requests::POST: + curl_setopt($this->fp, CURLOPT_POST, true); + curl_setopt($this->fp, CURLOPT_POSTFIELDS, $data); + break; + case Requests::PATCH: + case Requests::PUT: + curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, $options['type']); + curl_setopt($this->fp, CURLOPT_POSTFIELDS, $data); + break; + case Requests::DELETE: + curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + case Requests::HEAD: + curl_setopt($this->fp, CURLOPT_NOBODY, true); + break; + } + + curl_setopt($this->fp, CURLOPT_URL, $url); + curl_setopt($this->fp, CURLOPT_TIMEOUT, $options['timeout']); + curl_setopt($this->fp, CURLOPT_CONNECTTIMEOUT, $options['timeout']); + curl_setopt($this->fp, CURLOPT_REFERER, $url); + curl_setopt($this->fp, CURLOPT_USERAGENT, $options['useragent']); + curl_setopt($this->fp, CURLOPT_HTTPHEADER, $headers); + + if (true === $options['blocking']) { + curl_setopt($this->fp, CURLOPT_HEADERFUNCTION, array(&$this, 'stream_headers')); + } + } + + public function process_response($response, $options) { + if ($options['blocking'] === false) { + $fake_headers = ''; + $options['hooks']->dispatch('curl.after_request', array(&$fake_headers)); + return false; + } + if ($options['filename'] !== false) { + fclose($this->stream_handle); + $this->headers = trim($this->headers); + } + else { + $this->headers .= $response; + } + + if (curl_errno($this->fp)) { + throw new Requests_Exception('cURL error ' . curl_errno($this->fp) . ': ' . curl_error($this->fp), 'curlerror', $this->fp); + return; + } + $this->info = curl_getinfo($this->fp); + + $options['hooks']->dispatch('curl.after_request', array(&$this->headers)); + return $this->headers; + } + + /** + * Collect the headers as they are received + * + * @param resource $handle cURL resource + * @param string $headers Header string + * @return integer Length of provided header + */ + protected function stream_headers($handle, $headers) { + // Why do we do this? cURL will send both the final response and any + // interim responses, such as a 100 Continue. We don't need that. + // (We may want to keep this somewhere just in case) + if ($this->done_headers) { + $this->headers = ''; + $this->done_headers = false; + } + $this->headers .= $headers; + + if ($headers === "\r\n") { + $this->done_headers = true; + } + return strlen($headers); + } + + /** + * Format a URL given GET data + * + * @param string $url + * @param array|object $data Data to build query using, see {@see http://php.net/http_build_query} + * @return string URL with data + */ + protected static function format_get($url, $data) { + if (!empty($data)) { + $url_parts = parse_url($url); + if (empty($url_parts['query'])) { + $query = $url_parts['query'] = ''; + } + else { + $query = $url_parts['query']; + } + + $query .= '&' . http_build_query($data, null, '&'); + $query = trim($query, '&'); + + if (empty($url_parts['query'])) { + $url .= '?' . $query; + } + else { + $url = str_replace($url_parts['query'], $query, $url); + } + } + return $url; + } + + /** + * Whether this transport is valid + * + * @codeCoverageIgnore + * @return boolean True if the transport is valid, false otherwise. + */ + public static function test() { + return (function_exists('curl_init') && function_exists('curl_exec')); + } +} diff --git a/vendor/rmccue/requests/library/Requests/Transport/cacert.pem b/vendor/rmccue/requests/library/Requests/Transport/cacert.pem new file mode 100644 index 00000000000..56ece1a03e7 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Transport/cacert.pem @@ -0,0 +1,3554 @@ +## +## ca-bundle.crt -- Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Sat Dec 29 20:03:40 2012 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## + +# @(#) $RCSfile: certdata.txt,v $ $Revision: 1.87 $ $Date: 2012/12/29 16:32:45 $ + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +GTE CyberTrust Global Root +========================== +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg +Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG +A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz +MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL +Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 +IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u +sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql +HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW +M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF +NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +Thawte Server CA +================ +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE +AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j +b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV +BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u +c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG +A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl +/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 +1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J +GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ +GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte Premium Server CA +======================== +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE +AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl +ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU +VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 +aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ +cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh +Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ +qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm +SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf +8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t +UCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +Equifax Secure CA +================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE +ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT +B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR +fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW +8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE +CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS +spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 +zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB +BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 +70+sB3c4 +-----END CERTIFICATE----- + +Digital Signature Trust Co. Global CA 1 +======================================= +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE +ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEy +MTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs +IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJE +NySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2i +o74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo +BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 +dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw +IoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQY +MBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAM +BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB +ACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEq +kzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4 +RbyhkwS7hp86W0N6w4pl +-----END CERTIFICATE----- + +Digital Signature Trust Co. Global CA 3 +======================================= +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE +ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEy +MDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs +IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGOD +VvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JS +xhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo +BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 +dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw +IoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQY +MBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAM +BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB +AEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfi +up/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1 +mPnHfxsb1gYgAlihw6ID +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +ValiCert Class 1 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy +MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi +GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm +DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG +lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX +icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP +Orf1LXLI +-----END CERTIFICATE----- + +ValiCert Class 2 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC +CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf +ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ +SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV +UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 +W9ViH0Pd +-----END CERTIFICATE----- + +RSA Root Certificate 1 +====================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td +3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H +BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs +3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF +V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r +on+jjBXu +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Entrust.net Secure Server CA +============================ +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg +cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl +ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG +A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi +eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p +dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ +aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 +gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw +ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l +dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw +NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow +HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN +Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 +n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhC +AQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdER +gL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B +AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo +oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcS +o8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z +2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjX +OP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ== +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Equifax Secure Global eBusiness CA +================================== +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp +bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds +b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV +PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN +qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn +hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs +MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN +I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY +NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 1 +============================= +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB +LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE +ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz +IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ +1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a +IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk +MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW +Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF +AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 +lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ +KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 2 +============================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UE +ChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y +MB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoT +DkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn +2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5 +BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUx +JjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9e +uSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1 +jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia +78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUm +V+GRMOrN +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +RSA Security 2048 v3 +==================== +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK +ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy +MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb +BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 +Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb +WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH +KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP ++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E +FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY +v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj +0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj +VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 +nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +America Online Root Certification Authority 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG +v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z +DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh +sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP +8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z +o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf +GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF +VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft +3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds +sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America Online Root Certification Authority 2 +============================================= +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en +fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 +f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO +qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN +RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 +gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn +6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid +FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 +Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj +B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY +T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p ++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg +JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy +zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO +ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh +1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf +GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff +Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP +cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA +============================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE +ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w +HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh +bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt +vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P +jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca +C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth +vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 +22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV +HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v +dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN +BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR +EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw +MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +TDC Internet Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE +ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx +NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu +ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j +xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL +znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc +5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 +otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI +AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM +VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM +MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe +UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G +CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb +O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU +Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +UTN DATACorp SGC Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ +BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa +MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w +HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy +dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys +raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo +wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA +9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv +33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud +DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 +BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD +LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 +I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx +EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP +DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +NetLock Notary (Class A) Root +============================= +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI +EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j +ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX +DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH +EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD +VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz +cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM +D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ +z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC +/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 +tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 +4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG +A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC +Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv +bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn +LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 +ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz +IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh +IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu +b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg +Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp +bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 +ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP +ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB +CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr +KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM +8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +NetLock Business (Class B) Root +=============================== +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg +VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD +VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv +bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg +VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S +o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr +1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV +HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ +RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh +dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 +ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv +c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg +YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz +Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA +bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl +IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 +YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj +cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM +43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR +stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +NetLock Express (Class C) Root +============================== +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ +BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j +ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z +W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 +euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw +DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN +RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn +YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB +IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i +aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 +ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y +emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k +IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ +UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg +YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 +xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW +gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Firmaprofesional Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT +GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp +Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA +ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL +MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT +OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2 +ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V +j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH +lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf +3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8 +NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww +KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG +AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD +ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq +u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf +wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm +7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG +VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA= +-----END CERTIFICATE----- + +Wells Fargo Root CA +=================== +-----BEGIN CERTIFICATE----- +MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDAxMDExMTY0MTI4WhcNMjEwMTE0MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dl +bGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEv +MC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n135zHCLielTWi5MbqNQ1mX +x3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHESxP9cMIlrCL1dQu3U+SlK93OvRw6esP3 +E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4OJgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5 +OEL8pahbSCOz6+MlsoCultQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4j +sNtlAHCEAQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMBAAGj +YTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcBCzAyMDAGCCsGAQUF +BwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRwb2xpY3kwDQYJKoZIhvcNAQEFBQAD +ggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrv +m+0fazbuSCUlFLZWohDo7qd/0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0R +OhPs7fpvcmR7nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx +x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ33ZwmVxwQ023 +tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s= +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4 +MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM +MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF +NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe +AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC +b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn +7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN +cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp +WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5 +haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY +MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9 +MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn +jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ +MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H +VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl +vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl +OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3 +1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq +nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy +x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 1 +============================================== +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP +MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 +acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx +MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB +TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC +aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX +yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i +Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ +8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 +W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 +sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE +q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY +nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2 +============================================== +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN +MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr +dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe +LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI +x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g +QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr +5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB +AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt +Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ +hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P +9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 +UrbnBEI= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +WellsSecure Public Root Certificate Authority +============================================= +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM +F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw +NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl +bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD +VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 +iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 +i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 +bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB +K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB +AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu +cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm +lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB +i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww +GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI +K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 +bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj +qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es +E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ +tylv2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +IGC/A +===== +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD +VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE +Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy +MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI +EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT +STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 +TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW +So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy +HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd +frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ +tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB +egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC +iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK +q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q +MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI +lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF +0mBWWg== +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE +BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL +EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 +MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz +dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT +GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG +d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N +oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc +QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ +PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb +MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG +IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD +VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 +LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A +dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA +4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg +AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA +egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 +Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO +PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv +c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h +cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw +IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT +WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV +MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER +MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp +Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal +HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT +nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE +aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK +yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB +S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. +====================================== +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT +AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg +LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w +HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ +U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh +IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN +yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU +2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 +4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP +2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm +8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf +HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa +Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK +5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b +czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g +ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF +BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug +cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf +AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX +EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v +/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 +MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 +3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk +eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f +/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h +RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU +Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 2 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw +MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw +IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 +xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ +Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u +SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G +dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ +KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj +TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP +JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk +vQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 3 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw +MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W +yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo +6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ +uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk +2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE +O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 +yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 +IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal +092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc +5A== +-----END CERTIFICATE----- + +TC TrustCenter Universal CA I +============================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN +MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg +VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw +JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC +qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv +xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw +ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O +gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j +BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG +1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy +vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 +ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a +7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +ComSign Secured CA +================== +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE +AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w +NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD +QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs +49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH +7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB +kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 +9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw +AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t +U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA +j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC +AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a +BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp +FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP +51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +Buypass Class 2 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 +MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M +cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 +0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 +0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R +uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV +1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt +7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 +fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w +wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +Buypass Class 3 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 +MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx +ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 +n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia +AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c +1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 +pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA +EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 +htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj +el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 +========================================================================== +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg +QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe +Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt +IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by +X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b +gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr +eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ +TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy +Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn +uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI +qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm +ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0 +Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW +Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t +FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm +zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k +XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT +bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU +RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK +1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt +2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ +Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9 +AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +ApplicationCA - Japanese Government +=================================== +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT +SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw +MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl +cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 +fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN +wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE +jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu +nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU +WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV +BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD +vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs +o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g +/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD +io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW +dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) FÅ‘tanúsítvány +============================================ +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ +5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn +vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj +CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil +e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR +OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI +CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65 +48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi +trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737 +qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB +AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC +ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA +A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz ++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj +f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN +kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk +CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF +URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb +CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h +oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV +IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm +66+KAQ== +-----END CERTIFICATE----- + +CA Disig +======== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK +QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw +MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz +bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm +GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD +Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo +hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt +ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w +gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P +AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz +aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff +ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa +BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t +WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 +mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K +ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA +4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +Juur-SK +======= +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA +c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw +DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG +SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy +aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf +TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC ++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw +UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa +Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF +MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD +HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh +AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA +cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr +AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw +cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G +A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo +ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL +abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 +IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh +Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 +yyqcjg== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD +T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4 +MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG +A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk +WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD +YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew +MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb +m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk +HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT +xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2 +3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9 +2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq +TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz +4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU +9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg +aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP +eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk +zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1 +ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI +KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq +nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE +I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp +MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o +tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi +=================================================== +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz +ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 +MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 +cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u +aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY +8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y +jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI +JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk +9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG +SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d +F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq +D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 +Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +TC TrustCenter Universal CA III +=============================== +-----BEGIN CERTIFICATE----- +MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAe +Fw0wOTA5MDkwODE1MjdaFw0yOTEyMzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNU +QyBUcnVzdENlbnRlciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0Ex +KDAmBgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF5+cvAqBNLaT6hdqbJYUt +QCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYvDIRlzg9uwliT6CwLOunBjvvya8o84pxO +juT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8vzArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+Eut +CHnNaYlAJ/Uqwa1D7KRTyGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1 +M4BDj5yjdipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBhMB8G +A1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI4jANBgkqhkiG9w0BAQUFAAOCAQEA +g8ev6n9NCjw5sWi+e22JLumzCecYV42FmhfzdkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+ +KGwWaODIl0YgoGhnYIg5IFHYaAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhK +BgePxLcHsU0GDeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV +CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPHLQNjO9Po5KIq +woIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg== +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================= +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +Root CA Generalitat Valenciana +============================== +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE +ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 +IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 +WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE +CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 +F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B +ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ +D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte +JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB +AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n +dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB +ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl +AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA +YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy +AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt +AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA +YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu +AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA +OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 +dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV +BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S +b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh +TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz +Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 +NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH +iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt ++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +A-Trust-nQual-03 +================ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE +Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy +a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R +dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw +RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 +ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 +c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA +zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n +yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE +SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 +iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V +cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV +eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 +ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr +sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd +JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 +ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ +Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 +dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu +c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv +bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 +aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t +L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 +fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm +N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN +Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T +tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX +e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA +2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs +HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib +D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +StartCom Certification Authority G2 +=================================== +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE +ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O +o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG +4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi +Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul +Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs +O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H +vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L +nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS +FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa +z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ +KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk +J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ +JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG +/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc +nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld +blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc +l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm +7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm +obp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- diff --git a/vendor/rmccue/requests/library/Requests/Transport/fsockopen.php b/vendor/rmccue/requests/library/Requests/Transport/fsockopen.php new file mode 100644 index 00000000000..275e9593c17 --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Transport/fsockopen.php @@ -0,0 +1,394 @@ +dispatch('fsockopen.before_request'); + + $url_parts = parse_url($url); + $host = $url_parts['host']; + $context = stream_context_create(); + $verifyname = false; + + // HTTPS support + if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') { + $remote_socket = 'ssl://' . $host; + $url_parts['port'] = 443; + + $context_options = array( + 'verify_peer' => true, + // 'CN_match' => $host, + 'capture_peer_cert' => true + ); + $verifyname = true; + + // SNI, if enabled (OpenSSL >=0.9.8j) + if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) { + $context_options['SNI_enabled'] = true; + if (isset($options['verifyname']) && $options['verifyname'] === false) { + $context_options['SNI_enabled'] = false; + } + } + + if (isset($options['verify'])) { + if ($options['verify'] === false) { + $context_options['verify_peer'] = false; + } elseif (is_string($options['verify'])) { + $context_options['cafile'] = $options['verify']; + } + } + + if (isset($options['verifyname']) && $options['verifyname'] === false) { + $verifyname = false; + } + + stream_context_set_option($context, array('ssl' => $context_options)); + } + else { + $remote_socket = 'tcp://' . $host; + } + + $proxy = isset( $options['proxy'] ); + $proxy_auth = $proxy && isset( $options['proxy_username'] ) && isset( $options['proxy_password'] ); + + if (!isset($url_parts['port'])) { + $url_parts['port'] = 80; + } + $remote_socket .= ':' . $url_parts['port']; + + set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE); + + $options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket)); + + $fp = stream_socket_client($remote_socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $context); + + restore_error_handler(); + + if ($verifyname) { + if (!$this->verify_certificate_from_context($host, $context)) { + throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match'); + } + } + + if (!$fp) { + if ($errno === 0) { + // Connection issue + throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error'); + } + else { + throw new Requests_Exception($errstr, 'fsockopenerror'); + return; + } + } + + $request_body = ''; + $out = ''; + switch ($options['type']) { + case Requests::POST: + case Requests::PUT: + case Requests::PATCH: + if (isset($url_parts['path'])) { + $path = $url_parts['path']; + if (isset($url_parts['query'])) { + $path .= '?' . $url_parts['query']; + } + } + else { + $path = '/'; + } + + $options['hooks']->dispatch( 'fsockopen.remote_host_path', array( &$path, $url ) ); + $out = $options['type'] . " $path HTTP/1.0\r\n"; + + if (is_array($data)) { + $request_body = http_build_query($data, null, '&'); + } + else { + $request_body = $data; + } + if (empty($headers['Content-Length'])) { + $headers['Content-Length'] = strlen($request_body); + } + if (empty($headers['Content-Type'])) { + $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; + } + break; + case Requests::HEAD: + case Requests::GET: + case Requests::DELETE: + $path = self::format_get($url_parts, $data); + $options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url)); + $out = $options['type'] . " $path HTTP/1.0\r\n"; + break; + } + $out .= "Host: {$url_parts['host']}"; + + if ($url_parts['port'] !== 80) { + $out .= ":{$url_parts['port']}"; + } + $out .= "\r\n"; + + $out .= "User-Agent: {$options['useragent']}\r\n"; + $accept_encoding = $this->accept_encoding(); + if (!empty($accept_encoding)) { + $out .= "Accept-Encoding: $accept_encoding\r\n"; + } + + $headers = Requests::flatten($headers); + + if (!empty($headers)) { + $out .= implode($headers, "\r\n") . "\r\n"; + } + + $options['hooks']->dispatch('fsockopen.after_headers', array(&$out)); + + if (substr($out, -2) !== "\r\n") { + $out .= "\r\n"; + } + + $out .= "Connection: Close\r\n\r\n" . $request_body; + + $options['hooks']->dispatch('fsockopen.before_send', array(&$out)); + + fwrite($fp, $out); + $options['hooks']->dispatch('fsockopen.after_send', array(&$fake_headers)); + + if (!$options['blocking']) { + fclose($fp); + $fake_headers = ''; + $options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers)); + return ''; + } + stream_set_timeout($fp, $options['timeout']); + + $this->info = stream_get_meta_data($fp); + + $this->headers = ''; + $this->info = stream_get_meta_data($fp); + if (!$options['filename']) { + while (!feof($fp)) { + $this->info = stream_get_meta_data($fp); + if ($this->info['timed_out']) { + throw new Requests_Exception('fsocket timed out', 'timeout'); + } + + $this->headers .= fread($fp, 1160); + } + } + else { + $download = fopen($options['filename'], 'wb'); + $doingbody = false; + $response = ''; + while (!feof($fp)) { + $this->info = stream_get_meta_data($fp); + if ($this->info['timed_out']) { + throw new Requests_Exception('fsocket timed out', 'timeout'); + } + + $block = fread($fp, 1160); + if ($doingbody) { + fwrite($download, $block); + } + else { + $response .= $block; + if (strpos($response, "\r\n\r\n")) { + list($this->headers, $block) = explode("\r\n\r\n", $response, 2); + $doingbody = true; + fwrite($download, $block); + } + } + } + fclose($download); + } + fclose($fp); + + $options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers)); + return $this->headers; + } + + /** + * Send multiple requests simultaneously + * + * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request} + * @param array $options Global options, see {@see Requests::response()} for documentation + * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) + */ + public function request_multiple($requests, $options) { + $responses = array(); + $class = get_class($this); + foreach ($requests as $id => $request) { + try { + $handler = new $class(); + $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); + + $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request)); + } + catch (Requests_Exception $e) { + $responses[$id] = $e; + } + + if (!is_string($responses[$id])) { + $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id)); + } + } + + return $responses; + } + + /** + * Retrieve the encodings we can accept + * + * @return string Accept-Encoding header value + */ + protected static function accept_encoding() { + $type = array(); + if (function_exists('gzinflate')) { + $type[] = 'deflate;q=1.0'; + } + + if (function_exists('gzuncompress')) { + $type[] = 'compress;q=0.5'; + } + + $type[] = 'gzip;q=0.5'; + + return implode(', ', $type); + } + + /** + * Format a URL given GET data + * + * @param array $url_parts + * @param array|object $data Data to build query using, see {@see http://php.net/http_build_query} + * @return string URL with data + */ + protected static function format_get($url_parts, $data) { + if (!empty($data)) { + if (empty($url_parts['query'])) + $url_parts['query'] = ''; + + $url_parts['query'] .= '&' . http_build_query($data, null, '&'); + $url_parts['query'] = trim($url_parts['query'], '&'); + } + if (isset($url_parts['path'])) { + if (isset($url_parts['query'])) { + $get = $url_parts['path'] . '?' . $url_parts['query']; + } + else { + $get = $url_parts['path']; + } + } + else { + $get = '/'; + } + return $get; + } + + /** + * Error handler for stream_socket_client() + * + * @param int $errno Error number (e.g. E_WARNING) + * @param string $errstr Error message + */ + public function connect_error_handler($errno, $errstr) { + // Double-check we can handle it + if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) { + // Return false to indicate the default error handler should engage + return false; + } + + $this->connect_error .= $errstr . "\n"; + return true; + } + + /** + * Verify the certificate against common name and subject alternative names + * + * Unfortunately, PHP doesn't check the certificate against the alternative + * names, leading things like 'https://www.github.com/' to be invalid. + * Instead + * + * @see http://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 + * + * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`) + * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) + * @param string $host Host name to verify against + * @param resource $context Stream context + * @return bool + */ + public function verify_certificate_from_context($host, $context) { + $meta = stream_context_get_options($context); + + // If we don't have SSL options, then we couldn't make the connection at + // all + if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { + throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error'); + } + + $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); + + return Requests_SSL::verify_certificate($host, $cert); + } + + /** + * Whether this transport is valid + * + * @codeCoverageIgnore + * @return boolean True if the transport is valid, false otherwise. + */ + public static function test($capabilities = array()) { + if (!function_exists('fsockopen')) + return false; + + // If needed, check that streams support SSL + if (isset( $capabilities['ssl'] ) && $capabilities['ssl']) { + if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) + return false; + + // Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156 + if (defined('HHVM_VERSION')) + return false; + } + + return true; + } +} diff --git a/vendor/rmccue/requests/library/Requests/Utility/CaseInsensitiveDictionary.php b/vendor/rmccue/requests/library/Requests/Utility/CaseInsensitiveDictionary.php new file mode 100644 index 00000000000..f6e14966e7e --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Utility/CaseInsensitiveDictionary.php @@ -0,0 +1,91 @@ +data[$key]); + } + + /** + * Get the value for the item + * + * @param string $key Item key + * @return string Item value + */ + public function offsetGet($key) { + $key = strtolower($key); + if (!isset($this->data[$key])) + return null; + + return $this->data[$key]; + } + + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $key = strtolower($key); + $this->data[$key] = $value; + } + + /** + * Unset the given header + * + * @param string $key + */ + public function offsetUnset($key) { + unset($this->data[strtolower($key)]); + } + + /** + * Get an iterator for the data + * + * @return ArrayIterator + */ + public function getIterator() { + return new ArrayIterator($this->data); + } + + /** + * Get the headers as an array + * + * @return array Header data + */ + public function getAll() { + return $this->data; + } +} diff --git a/vendor/rmccue/requests/library/Requests/Utility/FilteredIterator.php b/vendor/rmccue/requests/library/Requests/Utility/FilteredIterator.php new file mode 100644 index 00000000000..41e2a3d32ce --- /dev/null +++ b/vendor/rmccue/requests/library/Requests/Utility/FilteredIterator.php @@ -0,0 +1,38 @@ +callback = $callback; + } + + /** + * Get the current item's value after filtering + * + * @return string + */ + public function current() { + $value = parent::current(); + $value = call_user_func($this->callback, $value); + return $value; + } +} diff --git a/vendor/rmccue/requests/package.xml.tpl b/vendor/rmccue/requests/package.xml.tpl new file mode 100644 index 00000000000..f1ccab6556b --- /dev/null +++ b/vendor/rmccue/requests/package.xml.tpl @@ -0,0 +1,60 @@ + + + Requests + pear.ryanmccue.info + A HTTP library written in PHP, for human beings. + + Requests is a HTTP library written in PHP, for human beings. It is + roughly based on the API from the excellent Requests Python library. + Requests is ISC Licensed (similar to the new BSD license) and has + no dependencies. + + + Ryan McCue + rmccue + me+pear@ryanmccue dot info + yes + + {{ date }} + + + {{ version }} + {{ api_version }} + + + {{ stability }} + {{ stability }} + + ISC + - + + + + + + + + +{{ files }} + + + + + + + + + 5.2.0 + + + 1.4.0 + + + + + \ No newline at end of file diff --git a/vendor/rmccue/requests/tests/Auth/Basic.php b/vendor/rmccue/requests/tests/Auth/Basic.php new file mode 100644 index 00000000000..9f19014cd25 --- /dev/null +++ b/vendor/rmccue/requests/tests/Auth/Basic.php @@ -0,0 +1,87 @@ +markTestSkipped($transport . ' is not available'); + return; + } + + $options = array( + 'auth' => array('user', 'passwd'), + 'transport' => $transport, + ); + $request = Requests::get('http://httpbin.org/basic-auth/user/passwd', array(), $options); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body); + $this->assertEquals(true, $result->authenticated); + $this->assertEquals('user', $result->user); + } + + /** + * @dataProvider transportProvider + */ + public function testUsingInstantiation($transport) { + if (!call_user_func(array($transport, 'test'))) { + $this->markTestSkipped($transport . ' is not available'); + return; + } + + $options = array( + 'auth' => new Requests_Auth_Basic(array('user', 'passwd')), + 'transport' => $transport, + ); + $request = Requests::get('http://httpbin.org/basic-auth/user/passwd', array(), $options); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body); + $this->assertEquals(true, $result->authenticated); + $this->assertEquals('user', $result->user); + } + + /** + * @dataProvider transportProvider + */ + public function testPOSTUsingInstantiation($transport) { + if (!call_user_func(array($transport, 'test'))) { + $this->markTestSkipped($transport . ' is not available'); + return; + } + + $options = array( + 'auth' => new Requests_Auth_Basic(array('user', 'passwd')), + 'transport' => $transport, + ); + $data = 'test'; + $request = Requests::post('http://httpbin.org/post', array(), $data, $options); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body); + + $auth = $result->headers->Authorization; + $auth = explode(' ', $auth); + + $this->assertEquals(base64_encode('user:passwd'), $auth[1]); + $this->assertEquals('test', $result->data); + } + + /** + * @expectedException Requests_Exception + */ + public function testMissingPassword() { + $auth = new Requests_Auth_Basic(array('user')); + } + +} \ No newline at end of file diff --git a/vendor/rmccue/requests/tests/ChunkedEncoding.php b/vendor/rmccue/requests/tests/ChunkedEncoding.php new file mode 100644 index 00000000000..fa875382619 --- /dev/null +++ b/vendor/rmccue/requests/tests/ChunkedEncoding.php @@ -0,0 +1,68 @@ +body = $body; + $transport->chunked = true; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + + $this->assertEquals($expected, $response->body); + } + + /** + * Response says it's chunked, but actually isn't + */ + public function testNotActuallyChunked() { + $transport = new MockTransport(); + $transport->body = 'Hello! This is a non-chunked response!'; + $transport->chunked = true; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + + $this->assertEquals($transport->body, $response->body); + } + + /** + * Response says it's chunked and starts looking like it is, but turns out + * that they're lying to us + */ + public function testMixedChunkiness() { + $transport = new MockTransport(); + $transport->body = "02\r\nab\r\nNot actually chunked!"; + $transport->chunked = true; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + $this->assertEquals($transport->body, $response->body); + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/tests/Cookies.php b/vendor/rmccue/requests/tests/Cookies.php new file mode 100644 index 00000000000..0348fd8001a --- /dev/null +++ b/vendor/rmccue/requests/tests/Cookies.php @@ -0,0 +1,174 @@ +assertEquals('requests-testcookie', $cookie->name); + $this->assertEquals('testvalue', $cookie->value); + $this->assertEquals('testvalue', (string) $cookie); + + $this->assertEquals('requests-testcookie=testvalue', $cookie->formatForHeader()); + $this->assertEquals('requests-testcookie=testvalue', $cookie->formatForSetCookie()); + } + + public function testCookieWithAttributes() { + $attributes = array( + 'httponly', + 'path' => '/' + ); + $cookie = new Requests_Cookie('requests-testcookie', 'testvalue', $attributes); + + $this->assertEquals('requests-testcookie=testvalue', $cookie->formatForHeader()); + $this->assertEquals('requests-testcookie=testvalue; httponly; path=/', $cookie->formatForSetCookie()); + } + + public function testEmptyCookieName() { + $cookie = Requests_Cookie::parse('test'); + $this->assertEquals('', $cookie->name); + $this->assertEquals('test', $cookie->value); + } + + public function testEmptyAttributes() { + $cookie = Requests_Cookie::parse('foo=bar; HttpOnly'); + $this->assertTrue($cookie->attributes['httponly']); + } + + public function testCookieJarSetter() { + $jar1 = new Requests_Cookie_Jar(); + $jar1['requests-testcookie'] = 'testvalue'; + + $jar2 = new Requests_Cookie_Jar(array( + 'requests-testcookie' => 'testvalue', + )); + $this->assertEquals($jar1, $jar2); + } + + public function testCookieJarUnsetter() { + $jar = new Requests_Cookie_Jar(); + $jar['requests-testcookie'] = 'testvalue'; + + $this->assertEquals('testvalue', $jar['requests-testcookie']); + + unset($jar['requests-testcookie']); + $this->assertEmpty($jar['requests-testcookie']); + $this->assertFalse(isset($jar['requests-testcookie'])); + } + + /** + * @expectedException Requests_Exception + */ + public function testCookieJarAsList() { + $cookies = new Requests_Cookie_Jar(); + $cookies[] = 'requests-testcookie1=testvalue1'; + } + + public function testCookieJarIterator() { + $cookies = array( + 'requests-testcookie1' => 'testvalue1', + 'requests-testcookie2' => 'testvalue2', + ); + $jar = new Requests_Cookie_Jar($cookies); + + foreach ($jar as $key => $value) { + $this->assertEquals($cookies[$key], $value); + } + } + + public function testReceivingCookies() { + $options = array( + 'follow_redirects' => false, + ); + $url = 'http://httpbin.org/cookies/set?requests-testcookie=testvalue'; + + $response = Requests::get($url, array(), $options); + + $cookie = $response->cookies['requests-testcookie']; + $this->assertNotEmpty( $cookie ); + $this->assertEquals( 'testvalue', $cookie->value ); + } + + public function testPersistenceOnRedirect() { + $options = array( + 'follow_redirects' => true, + ); + $url = 'http://httpbin.org/cookies/set?requests-testcookie=testvalue'; + + $response = Requests::get($url, array(), $options); + + $cookie = $response->cookies['requests-testcookie']; + $this->assertNotEmpty( $cookie ); + $this->assertEquals( 'testvalue', $cookie->value ); + } + + protected function setCookieRequest($cookies) { + $options = array( + 'cookies' => $cookies, + ); + $response = Requests::get('http://httpbin.org/cookies/set', array(), $options); + + $data = json_decode($response->body, true); + $this->assertInternalType('array', $data); + $this->assertArrayHasKey('cookies', $data); + return $data['cookies']; + } + + public function testSendingCookie() { + $cookies = array( + 'requests-testcookie1' => 'testvalue1', + ); + + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + } + + public function testSendingCookieWithJar() { + $cookies = new Requests_Cookie_Jar(array( + 'requests-testcookie1' => 'testvalue1', + )); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + } + + public function testSendingMultipleCookies() { + $cookies = array( + 'requests-testcookie1' => 'testvalue1', + 'requests-testcookie2' => 'testvalue2', + ); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + + $this->assertArrayHasKey('requests-testcookie2', $data); + $this->assertEquals('testvalue2', $data['requests-testcookie2']); + } + + public function testSendingMultipleCookiesWithJar() { + $cookies = new Requests_Cookie_Jar(array( + 'requests-testcookie1' => 'testvalue1', + 'requests-testcookie2' => 'testvalue2', + )); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + + $this->assertArrayHasKey('requests-testcookie2', $data); + $this->assertEquals('testvalue2', $data['requests-testcookie2']); + } + + public function testSendingPrebakedCookie() { + $cookies = new Requests_Cookie_Jar(array( + new Requests_Cookie('requests-testcookie', 'testvalue'), + )); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie', $data); + $this->assertEquals('testvalue', $data['requests-testcookie']); + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/tests/Encoding.php b/vendor/rmccue/requests/tests/Encoding.php new file mode 100644 index 00000000000..7e2f138496d --- /dev/null +++ b/vendor/rmccue/requests/tests/Encoding.php @@ -0,0 +1,94 @@ + $set) { + $real_set = self::mapData($key, $set); + $data = array_merge($data, $real_set); + } + return $data; + } + + /** + * @dataProvider encodedData + */ + public function testDecompress($original, $encoded) { + $decoded = Requests::decompress($encoded); + $this->assertEquals($original, $decoded); + } + + /** + * @dataProvider encodedData + */ + public function testCompatibleInflate($original, $encoded) { + $decoded = Requests::compatible_gzinflate($encoded); + $this->assertEquals($original, $decoded); + } + + protected function bin2hex($field) { + $field = bin2hex($field); + $field = chunk_split($field,2,"\\x"); + $field = "\\x" . substr($field,0,-2); + return $field; + } +} diff --git a/vendor/rmccue/requests/tests/IDNAEncoder.php b/vendor/rmccue/requests/tests/IDNAEncoder.php new file mode 100644 index 00000000000..24df2deffca --- /dev/null +++ b/vendor/rmccue/requests/tests/IDNAEncoder.php @@ -0,0 +1,102 @@ +assertEquals($expected, $result); + } + + /** + * @expectedException Requests_Exception + */ + public function testASCIITooLong() { + $data = str_repeat("abcd", 20); + $result = Requests_IDNAEncoder::encode($data); + } + + /** + * @expectedException Requests_Exception + */ + public function testEncodedTooLong() { + $data = str_repeat("\xe4\xbb\x96", 60); + $result = Requests_IDNAEncoder::encode($data); + } + + /** + * @expectedException Requests_Exception + */ + public function testAlreadyPrefixed() { + $result = Requests_IDNAEncoder::encode("xn--\xe4\xbb\x96"); + } + + public function testASCIICharacter() { + $result = Requests_IDNAEncoder::encode("a"); + $this->assertEquals('a', $result); + } + + public function testTwoByteCharacter() { + $result = Requests_IDNAEncoder::encode("\xc2\xb6"); // Pilcrow character + $this->assertEquals('xn--tba', $result); + } + + public function testThreeByteCharacter() { + $result = Requests_IDNAEncoder::encode("\xe2\x82\xac"); // Euro symbol + $this->assertEquals('xn--lzg', $result); + } + + public function testFourByteCharacter() { + $result = Requests_IDNAEncoder::encode("\xf0\xa4\xad\xa2"); // Chinese symbol? + $this->assertEquals('xn--ww6j', $result); + } + + /** + * @expectedException Requests_Exception + */ + public function testFiveByteCharacter() { + $result = Requests_IDNAEncoder::encode("\xfb\xb6\xb6\xb6\xb6"); + } + + /** + * @expectedException Requests_Exception + */ + public function testSixByteCharacter() { + $result = Requests_IDNAEncoder::encode("\xfd\xb6\xb6\xb6\xb6\xb6"); + } + + /** + * @expectedException Requests_Exception + */ + public function testInvalidASCIICharacterWithMultibyte() { + $result = Requests_IDNAEncoder::encode("\0\xc2\xb6"); + } + + /** + * @expectedException Requests_Exception + */ + public function testUnfinishedMultibyte() { + $result = Requests_IDNAEncoder::encode("\xc2"); + } + + /** + * @expectedException Requests_Exception + */ + public function testPartialMultibyte() { + $result = Requests_IDNAEncoder::encode("\xc2\xc2\xb6"); + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/tests/IRI.php b/vendor/rmccue/requests/tests/IRI.php new file mode 100644 index 00000000000..db0fef4f3c3 --- /dev/null +++ b/vendor/rmccue/requests/tests/IRI.php @@ -0,0 +1,418 @@ +assertEquals($expected, Requests_IRI::absolutize($base, $relative)->iri); + $this->assertEquals($expected, (string) Requests_IRI::absolutize($base, $relative)); + } + + /** + * @dataProvider rfc3986_tests + */ + public function testBothStringRFC3986($relative, $expected) + { + $base = 'http://a/b/c/d;p?q'; + $this->assertEquals($expected, Requests_IRI::absolutize($base, $relative)->iri); + $this->assertEquals($expected, (string) Requests_IRI::absolutize($base, $relative)); + } + + /** + * @dataProvider rfc3986_tests + */ + public function testObjectRFC3986($relative, $expected) + { + $base = new Requests_IRI('http://a/b/c/d;p?q'); + $expected = new Requests_IRI($expected); + $this->assertEquals($expected, Requests_IRI::absolutize($base, $relative)); + } + + public static function sp_tests() + { + return array( + array('http://a/b/c/d', 'f%0o', 'http://a/b/c/f%250o'), + array('http://a/b/', 'c', 'http://a/b/c'), + array('http://a/', 'b', 'http://a/b'), + array('http://a/', '/b', 'http://a/b'), + array('http://a/b', 'c', 'http://a/c'), + array('http://a/b/', "c\x0Ad", 'http://a/b/c%0Ad'), + array('http://a/b/', "c\x0A\x0B", 'http://a/b/c%0A%0B'), + array('http://a/b/c', '//0', 'http://0'), + array('http://a/b/c', '0', 'http://a/b/0'), + array('http://a/b/c', '?0', 'http://a/b/c?0'), + array('http://a/b/c', '#0', 'http://a/b/c#0'), + array('http://0/b/c', 'd', 'http://0/b/d'), + array('http://a/b/c?0', 'd', 'http://a/b/d'), + array('http://a/b/c#0', 'd', 'http://a/b/d'), + array('http://example.com', '//example.net', 'http://example.net'), + array('http:g', 'a', 'http:a'), + ); + } + + /** + * @dataProvider sp_tests + */ + public function testStringSP($base, $relative, $expected) + { + $base = new Requests_IRI($base); + $this->assertEquals($expected, Requests_IRI::absolutize($base, $relative)->iri); + $this->assertEquals($expected, (string) Requests_IRI::absolutize($base, $relative)); + } + + /** + * @dataProvider sp_tests + */ + public function testObjectSP($base, $relative, $expected) + { + $base = new Requests_IRI($base); + $expected = new Requests_IRI($expected); + $this->assertEquals($expected, Requests_IRI::absolutize($base, $relative)); + } + + public static function absolutize_tests() + { + return array( + array('http://example.com/', 'foo/111:bar', 'http://example.com/foo/111:bar'), + array('http://example.com/#foo', '', 'http://example.com'), + ); + } + + /** + * @dataProvider absolutize_tests + */ + public function testAbsolutizeString($base, $relative, $expected) + { + $base = new Requests_IRI($base); + $this->assertEquals($expected, Requests_IRI::absolutize($base, $relative)->iri); + } + + /** + * @dataProvider absolutize_tests + */ + public function testAbsolutizeObject($base, $relative, $expected) + { + $base = new Requests_IRI($base); + $expected = new Requests_IRI($expected); + $this->assertEquals($expected, Requests_IRI::absolutize($base, $relative)); + } + + public static function normalization_tests() + { + return array( + array('example://a/b/c/%7Bfoo%7D', 'example://a/b/c/%7Bfoo%7D'), + array('eXAMPLE://a/./b/../b/%63/%7bfoo%7d', 'example://a/b/c/%7Bfoo%7D'), + array('example://%61/', 'example://a/'), + array('example://%41/', 'example://a/'), + array('example://A/', 'example://a/'), + array('example://a/', 'example://a/'), + array('example://%25A/', 'example://%25a/'), + array('HTTP://EXAMPLE.com/', 'http://example.com'), + array('http://example.com/', 'http://example.com'), + array('http://example.com:', 'http://example.com'), + array('http://example.com:80', 'http://example.com'), + array('http://@example.com', 'http://@example.com'), + array('http://', 'http://'), + array('http://example.com?', 'http://example.com?'), + array('http://example.com#', 'http://example.com#'), + array('https://example.com/', 'https://example.com'), + array('https://example.com:', 'https://example.com'), + array('https://@example.com', 'https://@example.com'), + array('https://example.com?', 'https://example.com?'), + array('https://example.com#', 'https://example.com#'), + array('file://localhost/foobar', 'file:/foobar'), + array('http://[0:0:0:0:0:0:0:1]', 'http://[::1]'), + array('http://[2001:db8:85a3:0000:0000:8a2e:370:7334]', 'http://[2001:db8:85a3::8a2e:370:7334]'), + array('http://[0:0:0:0:0:ffff:c0a8:a01]', 'http://[::ffff:c0a8:a01]'), + array('http://[ffff:0:0:0:0:0:0:0]', 'http://[ffff::]'), + array('http://[::ffff:192.0.2.128]', 'http://[::ffff:192.0.2.128]'), + array('http://[invalid]', 'http:'), + array('http://[0:0:0:0:0:0:0:1]:', 'http://[::1]'), + array('http://[0:0:0:0:0:0:0:1]:80', 'http://[::1]'), + array('http://[0:0:0:0:0:0:0:1]:1234', 'http://[::1]:1234'), + // Punycode decoding helps with normalisation of IRIs, but is not + // needed for URIs, so we don't really care about it for Requests + //array('http://xn--tdali-d8a8w.lv', 'http://tūdaliņ.lv'), + //array('http://t%C5%ABdali%C5%86.lv', 'http://tūdaliņ.lv'), + array('http://Aa@example.com', 'http://Aa@example.com'), + array('http://example.com?Aa', 'http://example.com?Aa'), + array('http://example.com/Aa', 'http://example.com/Aa'), + array('http://example.com#Aa', 'http://example.com#Aa'), + array('http://[0:0:0:0:0:0:0:0]', 'http://[::]'), + array('http:.', 'http:'), + array('http:..', 'http:'), + array('http:./', 'http:'), + array('http:../', 'http:'), + array('http://example.com/%3A', 'http://example.com/%3A'), + array('http://example.com/:', 'http://example.com/:'), + array('http://example.com/%C2', 'http://example.com/%C2'), + array('http://example.com/%C2a', 'http://example.com/%C2a'), + array('http://example.com/%C2%00', 'http://example.com/%C2%00'), + array('http://example.com/%C3%A9', 'http://example.com/é'), + array('http://example.com/%C3%A9%00', 'http://example.com/é%00'), + array('http://example.com/%C3%A9cole', 'http://example.com/école'), + array('http://example.com/%FF', 'http://example.com/%FF'), + array("http://example.com/\xF3\xB0\x80\x80", 'http://example.com/%F3%B0%80%80'), + array("http://example.com/\xF3\xB0\x80\x80%00", 'http://example.com/%F3%B0%80%80%00'), + array("http://example.com/\xF3\xB0\x80\x80a", 'http://example.com/%F3%B0%80%80a'), + array("http://example.com?\xF3\xB0\x80\x80", "http://example.com?\xF3\xB0\x80\x80"), + array("http://example.com?\xF3\xB0\x80\x80%00", "http://example.com?\xF3\xB0\x80\x80%00"), + array("http://example.com?\xF3\xB0\x80\x80a", "http://example.com?\xF3\xB0\x80\x80a"), + array("http://example.com/\xEE\x80\x80", 'http://example.com/%EE%80%80'), + array("http://example.com/\xEE\x80\x80%00", 'http://example.com/%EE%80%80%00'), + array("http://example.com/\xEE\x80\x80a", 'http://example.com/%EE%80%80a'), + array("http://example.com?\xEE\x80\x80", "http://example.com?\xEE\x80\x80"), + array("http://example.com?\xEE\x80\x80%00", "http://example.com?\xEE\x80\x80%00"), + array("http://example.com?\xEE\x80\x80a", "http://example.com?\xEE\x80\x80a"), + array("http://example.com/\xC2", 'http://example.com/%C2'), + array("http://example.com/\xC2a", 'http://example.com/%C2a'), + array("http://example.com/\xC2\x00", 'http://example.com/%C2%00'), + array("http://example.com/\xC3\xA9", 'http://example.com/é'), + array("http://example.com/\xC3\xA9\x00", 'http://example.com/é%00'), + array("http://example.com/\xC3\xA9cole", 'http://example.com/école'), + array("http://example.com/\xFF", 'http://example.com/%FF'), + array("http://example.com/\xFF%00", 'http://example.com/%FF%00'), + array("http://example.com/\xFFa", 'http://example.com/%FFa'), + array('http://example.com/%61', 'http://example.com/a'), + array('http://example.com?%26', 'http://example.com?%26'), + array('http://example.com?%61', 'http://example.com?a'), + array('///', '///'), + ); + } + + /** + * @dataProvider normalization_tests + */ + public function testStringNormalization($input, $output) + { + $input = new Requests_IRI($input); + $this->assertEquals($output, $input->iri); + $this->assertEquals($output, (string) $input); + } + + /** + * @dataProvider normalization_tests + */ + public function testObjectNormalization($input, $output) + { + $input = new Requests_IRI($input); + $output = new Requests_IRI($output); + $this->assertEquals($output, $input); + } + + public static function equivalence_tests() + { + return array( + array('http://É.com', 'http://%C3%89.com'), + ); + } + + /** + * @dataProvider equivalence_tests + */ + public function testObjectEquivalence($input, $output) + { + $input = new Requests_IRI($input); + $output = new Requests_IRI($output); + $this->assertEquals($output, $input); + } + + public static function not_equivalence_tests() + { + return array( + array('http://example.com/foo/bar', 'http://example.com/foo%2Fbar'), + ); + } + + /** + * @dataProvider not_equivalence_tests + */ + public function testObjectNotEquivalence($input, $output) + { + $input = new Requests_IRI($input); + $output = new Requests_IRI($output); + $this->assertNotEquals($output, $input); + } + + public function testInvalidAbsolutizeBase() + { + $this->assertFalse(Requests_IRI::absolutize('://not a URL', '../')); + } + + public function testInvalidAbsolutizeRelative() + { + $this->assertFalse(Requests_IRI::absolutize('http://example.com/', 'http://example.com//not a URL')); + } + + public function testFullGamut() + { + $iri = new Requests_IRI(); + $iri->scheme = 'http'; + $iri->userinfo = 'user:password'; + $iri->host = 'example.com'; + $iri->path = '/test/'; + $iri->fragment = 'test'; + + $this->assertEquals('http', $iri->scheme); + $this->assertEquals('user:password', $iri->userinfo); + $this->assertEquals('example.com', $iri->host); + $this->assertEquals(80, $iri->port); + $this->assertEquals('/test/', $iri->path); + $this->assertEquals('test', $iri->fragment); + } + + public function testReadAliased() + { + $iri = new Requests_IRI(); + $iri->scheme = 'http'; + $iri->userinfo = 'user:password'; + $iri->host = 'example.com'; + $iri->path = '/test/'; + $iri->fragment = 'test'; + + $this->assertEquals('http', $iri->ischeme); + $this->assertEquals('user:password', $iri->iuserinfo); + $this->assertEquals('example.com', $iri->ihost); + $this->assertEquals(80, $iri->iport); + $this->assertEquals('/test/', $iri->ipath); + $this->assertEquals('test', $iri->ifragment); + } + + public function testWriteAliased() + { + $iri = new Requests_IRI(); + $iri->scheme = 'http'; + $iri->iuserinfo = 'user:password'; + $iri->ihost = 'example.com'; + $iri->ipath = '/test/'; + $iri->ifragment = 'test'; + + $this->assertEquals('http', $iri->scheme); + $this->assertEquals('user:password', $iri->userinfo); + $this->assertEquals('example.com', $iri->host); + $this->assertEquals(80, $iri->port); + $this->assertEquals('/test/', $iri->path); + $this->assertEquals('test', $iri->fragment); + } + + /** + * @expectedException PHPUnit_Framework_Error_Notice + */ + public function testNonexistantProperty() + { + $iri = new Requests_IRI(); + $this->assertFalse(isset($iri->nonexistant_prop)); + $should_fail = $iri->nonexistant_prop; + } + + public function testBlankHost() + { + $iri = new Requests_IRI('http://example.com/a/?b=c#d'); + $iri->host = null; + + $this->assertEquals(null, $iri->host); + $this->assertEquals('http:/a/?b=c#d', (string) $iri); + } + + public function testBadPort() + { + $iri = new Requests_IRI(); + $iri->port = 'example'; + + $this->assertEquals(null, $iri->port); + } +} diff --git a/vendor/rmccue/requests/tests/Requests.php b/vendor/rmccue/requests/tests/Requests.php new file mode 100644 index 00000000000..e13698ecd18 --- /dev/null +++ b/vendor/rmccue/requests/tests/Requests.php @@ -0,0 +1,140 @@ +assertEquals(200, $request->status_code); + } + + /** + * Standard response header parsing + */ + public function testHeaderParsing() { + $transport = new RawTransport(); + $transport->data = + "HTTP/1.0 200 OK\r\n". + "Host: localhost\r\n". + "Host: ambiguous\r\n". + "Nospace:here\r\n". + "Muchspace: there \r\n". + "Empty:\r\n". + "Empty2: \r\n". + "Folded: one\r\n". + "\ttwo\r\n". + " three\r\n\r\n". + "stop\r\n"; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + $expected = new Requests_Response_Headers(); + $expected['host'] = 'localhost,ambiguous'; + $expected['nospace'] = 'here'; + $expected['muchspace'] = 'there'; + $expected['empty'] = ''; + $expected['empty2'] = ''; + $expected['folded'] = 'one two three'; + foreach ($expected as $key => $value) { + $this->assertEquals($value, $response->headers[$key]); + } + + foreach ($response->headers as $key => $value) { + $this->assertEquals($value, $expected[$key]); + } + } + + public function testRawAccess() { + $transport = new RawTransport(); + $transport->data = + "HTTP/1.0 200 OK\r\n". + "Host: localhost\r\n\r\n". + "Test"; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + $this->assertEquals($transport->data, $response->raw); + } + + /** + * Headers with only \n delimiting should be treated as if they're \r\n + */ + public function testHeaderOnlyLF() { + $transport = new RawTransport(); + $transport->data = "HTTP/1.0 200 OK\r\nTest: value\nAnother-Test: value\r\n\r\n"; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + $this->assertEquals('value', $response->headers['test']); + $this->assertEquals('value', $response->headers['another-test']); + } + + /** + * Check that invalid protocols are not accepted + * + * We do not support HTTP/0.9. If this is really an issue for you, file a + * new issue, and update your server/proxy to support a proper protocol. + * + * @expectedException Requests_Exception + */ + public function testInvalidProtocolVersion() { + $transport = new RawTransport(); + $transport->data = "HTTP/0.9 200 OK\r\n\r\n

Test"; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + } + + /** + * HTTP/0.9 also appears to use a single CRLF instead of two + * + * @expectedException Requests_Exception + */ + public function testSingleCRLFSeparator() { + $transport = new RawTransport(); + $transport->data = "HTTP/0.9 200 OK\r\n

Test"; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + } + + /** + * @expectedException Requests_Exception + */ + public function testInvalidStatus() { + $transport = new RawTransport(); + $transport->data = "HTTP/1.1 OK\r\nTest: value\nAnother-Test: value\r\n\r\nTest"; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + } + + public function test30xWithoutLocation() { + $transport = new MockTransport(); + $transport->code = 302; + + $options = array( + 'transport' => $transport + ); + $response = Requests::get('http://example.com/', array(), $options); + $this->assertEquals(302, $response->status_code); + $this->assertEquals(0, $response->redirects); + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/tests/Response/Headers.php b/vendor/rmccue/requests/tests/Response/Headers.php new file mode 100644 index 00000000000..7541f374a3a --- /dev/null +++ b/vendor/rmccue/requests/tests/Response/Headers.php @@ -0,0 +1,55 @@ +assertEquals('text/plain', $headers['Content-Type']); + } + public function testCaseInsensitiveArrayAccess() { + $headers = new Requests_Response_Headers(); + $headers['Content-Type'] = 'text/plain'; + + $this->assertEquals('text/plain', $headers['CONTENT-TYPE']); + $this->assertEquals('text/plain', $headers['content-type']); + } + + /** + * @depends testArrayAccess + */ + public function testIteration() { + $headers = new Requests_Response_Headers(); + $headers['Content-Type'] = 'text/plain'; + $headers['Content-Length'] = 10; + + foreach ($headers as $name => $value) { + switch (strtolower($name)) { + case 'content-type': + $this->assertEquals('text/plain', $value); + break; + case 'content-length': + $this->assertEquals(10, $value); + break; + default: + throw new Exception('Invalid name: ' . $name); + } + } + } + + /** + * @expectedException Requests_Exception + */ + public function testInvalidKey() { + $headers = new Requests_Response_Headers(); + $headers[] = 'text/plain'; + } + + public function testMultipleHeaders() { + $headers = new Requests_Response_Headers(); + $headers['Accept'] = 'text/html;q=1.0'; + $headers['Accept'] = '*/*;q=0.1'; + + $this->assertEquals('text/html;q=1.0,*/*;q=0.1', $headers['Accept']); + } +} \ No newline at end of file diff --git a/vendor/rmccue/requests/tests/SSL.php b/vendor/rmccue/requests/tests/SSL.php new file mode 100644 index 00000000000..62bb998df32 --- /dev/null +++ b/vendor/rmccue/requests/tests/SSL.php @@ -0,0 +1,108 @@ +assertTrue(Requests_SSL::match_domain($base, $dnsname)); + } + + /** + * @dataProvider domainNoMatchProvider + */ + public function testNoMatch($base, $dnsname) { + $this->assertFalse(Requests_SSL::match_domain($base, $dnsname)); + } + + protected function fakeCertificate($dnsname, $with_san = true) { + $certificate = array( + 'subject' => array( + 'CN' => $dnsname + ), + ); + + if ($with_san !== false) { + // If SAN is set to true, default it to the dNSName + if ($with_san === true) { + $with_san = $dnsname; + } + $certificate['extensions'] = array( + 'subjectAltName' => 'DNS: ' . $with_san, + ); + } + + return $certificate; + } + + /** + * @dataProvider domainMatchProvider + */ + public function testMatchViaCertificate($base, $dnsname) { + $certificate = $this->fakeCertificate($dnsname); + $this->assertTrue(Requests_SSL::verify_certificate($base, $certificate)); + } + + /** + * @dataProvider domainNoMatchProvider + */ + public function testNoMatchViaCertificate($base, $dnsname) { + $certificate = $this->fakeCertificate($dnsname); + $this->assertFalse(Requests_SSL::verify_certificate($base, $certificate)); + } + + public function testCNFallback() { + $certificate = $this->fakeCertificate('example.com', false); + $this->assertTrue(Requests_SSL::verify_certificate('example.com', $certificate)); + } + + public function testInvalidCNFallback() { + $certificate = $this->fakeCertificate('example.com', false); + $this->assertFalse(Requests_SSL::verify_certificate('example.net', $certificate)); + } + + /** + * Test a certificate with both CN and SAN fields + * + * As per RFC2818, if the SAN field exists, we should parse that and ignore + * the value of the CN field. + * + * @link http://tools.ietf.org/html/rfc2818#section-3.1 + */ + public function testIgnoreCNWithSAN() { + $certificate = $this->fakeCertificate('example.net', 'example.com'); + + $this->assertTrue(Requests_SSL::verify_certificate('example.com', $certificate), 'Checking SAN validation'); + $this->assertFalse(Requests_SSL::verify_certificate('example.net', $certificate), 'Checking CN non-validation'); + } +} diff --git a/vendor/rmccue/requests/tests/Session.php b/vendor/rmccue/requests/tests/Session.php new file mode 100644 index 00000000000..676c609da30 --- /dev/null +++ b/vendor/rmccue/requests/tests/Session.php @@ -0,0 +1,86 @@ + 'testing', + 'X-TestHeader2' => 'requests-test' + ); + $data = array( + 'testdata' => 'value1', + 'test2' => 'value2', + 'test3' => array( + 'foo' => 'bar', + 'abc' => 'xyz' + ) + ); + $options = array( + 'testoption' => 'test', + 'foo' => 'bar' + ); + + $session = new Requests_Session('http://example.com/', $headers, $data, $options); + $this->assertEquals('http://example.com/', $session->url); + $this->assertEquals($headers, $session->headers); + $this->assertEquals($data, $session->data); + $this->assertEquals($options['testoption'], $session->options['testoption']); + + // Test via property access + $this->assertEquals($options['testoption'], $session->testoption); + + // Test setting new property + $session->newoption = 'foobar'; + $options['newoption'] = 'foobar'; + $this->assertEquals($options['newoption'], $session->options['newoption']); + + // Test unsetting property + unset($session->newoption); + $this->assertFalse(isset($session->newoption)); + + // Update property + $session->testoption = 'foobar'; + $options['testoption'] = 'foobar'; + $this->assertEquals($options['testoption'], $session->testoption); + + // Test getting invalid property + $this->assertNull($session->invalidoption); + } + + public function testURLResolution() { + $session = new Requests_Session('http://httpbin.org/'); + + // Set the cookies up + $response = $session->get('/get'); + $this->assertTrue($response->success); + $this->assertEquals('http://httpbin.org/get', $response->url); + + $data = json_decode($response->body, true); + $this->assertNotNull($data); + $this->assertArrayHasKey('url', $data); + $this->assertEquals('http://httpbin.org/get', $data['url']); + } + + public function testSharedCookies() { + $session = new Requests_Session('http://httpbin.org/'); + + $options = array( + 'follow_redirects' => false + ); + $response = $session->get('/cookies/set?requests-testcookie=testvalue', array(), $options); + $this->assertEquals(302, $response->status_code); + + // Check the cookies + $response = $session->get('/cookies'); + $this->assertTrue($response->success); + + // Check the response + $data = json_decode($response->body, true); + $this->assertNotNull($data); + $this->assertArrayHasKey('cookies', $data); + + $cookies = array( + 'requests-testcookie' => 'testvalue' + ); + $this->assertEquals($cookies, $data['cookies']); + } +} diff --git a/vendor/rmccue/requests/tests/Transport/Base.php b/vendor/rmccue/requests/tests/Transport/Base.php new file mode 100644 index 00000000000..8584efbdbd4 --- /dev/null +++ b/vendor/rmccue/requests/tests/Transport/Base.php @@ -0,0 +1,683 @@ +transport, 'test'); + $supported = call_user_func($callback); + + if (!$supported) { + $this->markTestSkipped($this->transport . ' is not available'); + return; + } + + $ssl_supported = call_user_func($callback, array('ssl' => true)); + if (!$ssl_supported) { + $this->skip_https = true; + } + } + protected $skip_https = false; + + protected function getOptions($other = array()) { + $options = array( + 'transport' => $this->transport + ); + $options = array_merge($options, $other); + return $options; + } + + public function testSimpleGET() { + $request = Requests::get('http://httpbin.org/get', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/get', $result['url']); + $this->assertEmpty($result['args']); + } + + public function testGETWithArgs() { + $request = Requests::get('http://httpbin.org/get?test=true&test2=test', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/get?test=true&test2=test', $result['url']); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['args']); + } + + public function testGETWithData() { + $data = array( + 'test' => 'true', + 'test2' => 'test', + ); + $request = Requests::request('http://httpbin.org/get', array(), $data, Requests::GET, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/get?test=true&test2=test', $result['url']); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['args']); + } + + public function testGETWithNestedData() { + $this->markTestSkipped('httpbin changed their data format; this test will now fail'); + $data = array( + 'test' => 'true', + 'test2' => array( + 'test3' => 'test', + 'test4' => 'test-too', + ), + ); + $request = Requests::request('http://httpbin.org/get', array(), $data, Requests::GET, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/get?test=true&test2%5Btest3%5D=test&test2%5Btest4%5D=test-too', $result['url']); + $this->assertEquals(array('test' => 'true', 'test2[test3]' => 'test', 'test2[test4]' => 'test-too'), $result['args']); + } + + public function testGETWithDataAndQuery() { + $data = array( + 'test2' => 'test', + ); + $request = Requests::request('http://httpbin.org/get?test=true', array(), $data, Requests::GET, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/get?test=true&test2=test', $result['url']); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['args']); + } + + public function testGETWithHeaders() { + $headers = array( + 'Requested-At' => time(), + ); + $request = Requests::get('http://httpbin.org/get', $headers, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals($headers['Requested-At'], $result['headers']['Requested-At']); + } + + public function testChunked() { + $request = Requests::get('http://httpbin.org/stream/1', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/stream/1', $result['url']); + $this->assertEmpty($result['args']); + } + + public function testHEAD() { + $request = Requests::head('http://httpbin.org/get', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + $this->assertEquals('', $request->body); + } + + public function testRawPOST() { + $data = 'test'; + $request = Requests::post('http://httpbin.org/post', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('test', $result['data']); + } + + public function testFormPost() { + $data = 'test=true&test2=test'; + $request = Requests::post('http://httpbin.org/post', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']); + } + + public function testPOSTWithArray() { + $data = array( + 'test' => 'true', + 'test2' => 'test', + ); + $request = Requests::post('http://httpbin.org/post', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']); + } + + public function testPOSTWithNestedData() { + $data = array( + 'test' => 'true', + 'test2' => array( + 'test3' => 'test', + 'test4' => 'test-too', + ), + ); + $request = Requests::post('http://httpbin.org/post', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals(array('test' => 'true', 'test2[test3]' => 'test', 'test2[test4]' => 'test-too'), $result['form']); + } + + public function testRawPUT() { + $data = 'test'; + $request = Requests::put('http://httpbin.org/put', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('test', $result['data']); + } + + public function testFormPUT() { + $data = 'test=true&test2=test'; + $request = Requests::put('http://httpbin.org/put', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']); + } + + public function testPUTWithArray() { + $data = array( + 'test' => 'true', + 'test2' => 'test', + ); + $request = Requests::put('http://httpbin.org/put', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']); + } + + public function testRawPATCH() { + $data = 'test'; + $request = Requests::patch('http://httpbin.org/patch', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('test', $result['data']); + } + + public function testFormPATCH() { + $data = 'test=true&test2=test'; + $request = Requests::patch('http://httpbin.org/patch', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code, $request->body); + + $result = json_decode($request->body, true); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']); + } + + public function testPATCHWithArray() { + $data = array( + 'test' => 'true', + 'test2' => 'test', + ); + $request = Requests::patch('http://httpbin.org/patch', array(), $data, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']); + } + + public function testDELETE() { + $request = Requests::delete('http://httpbin.org/delete', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/delete', $result['url']); + $this->assertEmpty($result['args']); + } + + public function testDELETEWithData() { + $data = array( + 'test' => 'true', + 'test2' => 'test', + ); + $request = Requests::request('http://httpbin.org/delete', array(), $data, Requests::DELETE, $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/delete?test=true&test2=test', $result['url']); + $this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['args']); + } + + public function testRedirects() { + $request = Requests::get('http://httpbin.org/redirect/6', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $this->assertEquals(6, $request->redirects); + } + + public function testRelativeRedirects() { + $request = Requests::get('http://httpbin.org/relative-redirect/6', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $this->assertEquals(6, $request->redirects); + } + + /** + * @expectedException Requests_Exception + * @todo This should also check that the type is "toomanyredirects" + */ + public function testTooManyRedirects() { + $options = array( + 'redirects' => 10, // default, but force just in case + ); + $request = Requests::get('http://httpbin.org/redirect/11', array(), $this->getOptions($options)); + } + + public static function statusCodeSuccessProvider() { + return array( + array(200, true), + array(201, true), + array(202, true), + array(203, true), + array(204, true), + array(205, true), + array(206, true), + array(300, false), + array(301, false), + array(302, false), + array(303, false), + array(304, false), + array(305, false), + array(306, false), + array(307, false), + array(400, false), + array(401, false), + array(402, false), + array(403, false), + array(404, false), + array(405, false), + array(406, false), + array(407, false), + array(408, false), + array(409, false), + array(410, false), + array(411, false), + array(412, false), + array(413, false), + array(414, false), + array(415, false), + array(416, false), + array(417, false), + array(418, false), // RFC 2324 + array(428, false), // RFC 6585 + array(429, false), // RFC 6585 + array(431, false), // RFC 6585 + array(500, false), + array(501, false), + array(502, false), + array(503, false), + array(504, false), + array(505, false), + array(511, false), // RFC 6585 + ); + } + + /** + * @dataProvider statusCodeSuccessProvider + */ + public function testStatusCode($code, $success) { + $url = sprintf('http://httpbin.org/status/%d', $code); + $options = array( + 'follow_redirects' => false, + ); + $request = Requests::get($url, array(), $this->getOptions($options)); + $this->assertEquals($code, $request->status_code); + $this->assertEquals($success, $request->success); + } + + /** + * @dataProvider statusCodeSuccessProvider + */ + public function testStatusCodeThrow($code, $success) { + $url = sprintf('http://httpbin.org/status/%d', $code); + $options = array( + 'follow_redirects' => false, + ); + + if (!$success) { + if ($code >= 400) { + $this->setExpectedException('Requests_Exception_HTTP_' . $code, $code); + } + elseif ($code >= 300 && $code < 400) { + $this->setExpectedException('Requests_Exception'); + } + } + $request = Requests::get($url, array(), $this->getOptions($options)); + $request->throw_for_status(false); + } + + /** + * @dataProvider statusCodeSuccessProvider + */ + public function testStatusCodeThrowAllowRedirects($code, $success) { + $url = sprintf('http://httpbin.org/status/%d', $code); + $options = array( + 'follow_redirects' => false, + ); + + if (!$success) { + if ($code >= 400) { + $this->setExpectedException('Requests_Exception_HTTP_' . $code, $code); + } + } + $request = Requests::get($url, array(), $this->getOptions($options)); + $request->throw_for_status(true); + } + + public function testStatusCodeUnknown(){ + $request = Requests::get('http://httpbin.org/status/599', array(), $this->getOptions()); + $this->assertEquals(599, $request->status_code); + $this->assertEquals(false, $request->success); + } + + /** + * @expectedException Requests_Exception_HTTP_Unknown + */ + public function testStatusCodeThrowUnknown(){ + $request = Requests::get('http://httpbin.org/status/599', array(), $this->getOptions()); + $request->throw_for_status(true); + } + + public function testGzipped() { + $request = Requests::get('http://httpbin.org/gzip', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body); + $this->assertEquals(true, $result->gzipped); + } + + public function testStreamToFile() { + $options = array( + 'filename' => tempnam(sys_get_temp_dir(), 'RLT') // RequestsLibraryTest + ); + $request = Requests::get('http://httpbin.org/get', array(), $this->getOptions($options)); + $this->assertEquals(200, $request->status_code); + $this->assertEmpty($request->body); + + $contents = file_get_contents($options['filename']); + $result = json_decode($contents, true); + $this->assertEquals('http://httpbin.org/get', $result['url']); + $this->assertEmpty($result['args']); + + unlink($options['filename']); + } + + public function testNonblocking() { + $options = array( + 'blocking' => false + ); + $request = Requests::get('http://httpbin.org/get', array(), $this->getOptions($options)); + $empty = new Requests_Response(); + $this->assertEquals($empty, $request); + } + + /** + * @expectedException Requests_Exception + */ + public function testBadIP() { + $request = Requests::get('http://256.256.256.0/', array(), $this->getOptions()); + } + + public function testHTTPS() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + + $request = Requests::get('https://httpbin.org/get', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + + $result = json_decode($request->body, true); + $this->assertEquals('http://httpbin.org/get', $result['url']); + $this->assertEmpty($result['args']); + } + + /** + * @expectedException Requests_Exception + */ + public function testExpiredHTTPS() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + + $request = Requests::get('https://testssl-expire.disig.sk/index.en.html', array(), $this->getOptions()); + } + + /** + * @expectedException Requests_Exception + */ + public function testRevokedHTTPS() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + + $request = Requests::get('https://testssl-revoked.disig.sk/index.en.html', array(), $this->getOptions()); + } + + /** + * Test that SSL fails with a bad certificate + * + * This is defined as invalid by + * https://onlinessl.netlock.hu/en/test-center/invalid-ssl-certificate.html + * and is used in testing in PhantomJS. That said, expect this to break. + * + * @expectedException Requests_Exception + */ + public function testBadDomain() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + + $request = Requests::get('https://tv.eurosport.com/', array(), $this->getOptions()); + } + + /** + * Test that the transport supports Server Name Indication with HTTPS + * + * sni.velox.ch is used for SNI testing, and the common name is set to + * `*.sni.velox.ch` as such. Without alternate name support, this will fail + * as `sni.velox.ch` is only in the alternate name + */ + public function testAlternateNameSupport() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + + $request = Requests::get('https://sni.velox.ch/', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + } + + /** + * Test that the transport supports Server Name Indication with HTTPS + * + * sni.velox.ch is used for SNI testing, and the common name is set to + * `*.sni.velox.ch` as such. Without SNI support, this will fail. Also tests + * our wildcard support. + */ + public function testSNISupport() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + + $request = Requests::get('https://abc.sni.velox.ch/', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + } + + /** + * @expectedException Requests_Exception + */ + public function testTimeout() { + $options = array( + 'timeout' => 1, + ); + $request = Requests::get('http://httpbin.org/delay/10', array(), $this->getOptions($options)); + var_dump($request); + } + + public function testMultiple() { + $requests = array( + 'test1' => array( + 'url' => 'http://httpbin.org/get' + ), + 'test2' => array( + 'url' => 'http://httpbin.org/get' + ), + ); + $responses = Requests::request_multiple($requests, $this->getOptions()); + + // test1 + $this->assertNotEmpty($responses['test1']); + $this->assertInstanceOf('Requests_Response', $responses['test1']); + $this->assertEquals(200, $responses['test1']->status_code); + + $result = json_decode($responses['test1']->body, true); + $this->assertEquals('http://httpbin.org/get', $result['url']); + $this->assertEmpty($result['args']); + + // test2 + $this->assertNotEmpty($responses['test2']); + $this->assertInstanceOf('Requests_Response', $responses['test2']); + $this->assertEquals(200, $responses['test2']->status_code); + + $result = json_decode($responses['test2']->body, true); + $this->assertEquals('http://httpbin.org/get', $result['url']); + $this->assertEmpty($result['args']); + } + + public function testMultipleWithDifferingMethods() { + $requests = array( + 'get' => array( + 'url' => 'http://httpbin.org/get', + ), + 'post' => array( + 'url' => 'http://httpbin.org/post', + 'type' => Requests::POST, + 'data' => 'test', + ), + ); + $responses = Requests::request_multiple($requests, $this->getOptions()); + + // get + $this->assertEquals(200, $responses['get']->status_code); + + // post + $this->assertEquals(200, $responses['post']->status_code); + $result = json_decode($responses['post']->body, true); + $this->assertEquals('test', $result['data']); + } + + /** + * @depends testTimeout + */ + public function testMultipleWithFailure() { + $requests = array( + 'success' => array( + 'url' => 'http://httpbin.org/get', + ), + 'timeout' => array( + 'url' => 'http://httpbin.org/delay/10', + 'options' => array( + 'timeout' => 1, + ), + ), + ); + $responses = Requests::request_multiple($requests, $this->getOptions()); + $this->assertEquals(200, $responses['success']->status_code); + $this->assertInstanceOf('Requests_Exception', $responses['timeout']); + } + + public function testMultipleUsingCallback() { + $requests = array( + 'get' => array( + 'url' => 'http://httpbin.org/get', + ), + 'post' => array( + 'url' => 'http://httpbin.org/post', + 'type' => Requests::POST, + 'data' => 'test', + ), + ); + $this->completed = array(); + $options = array( + 'complete' => array($this, 'completeCallback'), + ); + $responses = Requests::request_multiple($requests, $this->getOptions($options)); + + $this->assertEquals($this->completed, $responses); + $this->completed = array(); + } + + public function testMultipleUsingCallbackAndFailure() { + $requests = array( + 'success' => array( + 'url' => 'http://httpbin.org/get', + ), + 'timeout' => array( + 'url' => 'http://httpbin.org/delay/10', + 'options' => array( + 'timeout' => 1, + ), + ), + ); + $this->completed = array(); + $options = array( + 'complete' => array($this, 'completeCallback'), + ); + $responses = Requests::request_multiple($requests, $this->getOptions($options)); + + $this->assertEquals($this->completed, $responses); + $this->completed = array(); + } + + public function completeCallback($response, $key) { + $this->completed[$key] = $response; + } + + public function testMultipleToFile() { + $requests = array( + 'get' => array( + 'url' => 'http://httpbin.org/get', + 'options' => array( + 'filename' => tempnam(sys_get_temp_dir(), 'RLT') // RequestsLibraryTest + ), + ), + 'post' => array( + 'url' => 'http://httpbin.org/post', + 'type' => Requests::POST, + 'data' => 'test', + 'options' => array( + 'filename' => tempnam(sys_get_temp_dir(), 'RLT') // RequestsLibraryTest + ), + ), + ); + $responses = Requests::request_multiple($requests, $this->getOptions()); + + // GET request + $contents = file_get_contents($requests['get']['options']['filename']); + $result = json_decode($contents, true); + $this->assertEquals('http://httpbin.org/get', $result['url']); + $this->assertEmpty($result['args']); + unlink($requests['get']['options']['filename']); + + // POST request + $contents = file_get_contents($requests['post']['options']['filename']); + $result = json_decode($contents, true); + $this->assertEquals('http://httpbin.org/post', $result['url']); + $this->assertEquals('test', $result['data']); + unlink($requests['post']['options']['filename']); + } + + public function testHostHeader() { + $request = Requests::get('http://portquiz.positon.org:8080/', array(), $this->getOptions()); + $responseDoc = new DOMDocument; + $responseDoc->loadHTML($request->body); + $portXpath = new DOMXPath($responseDoc); + $portXpathMatches = $portXpath->query('//p/b'); + $this->assertEquals(8080, $portXpathMatches->item(0)->nodeValue); + } +} diff --git a/vendor/rmccue/requests/tests/Transport/cURL.php b/vendor/rmccue/requests/tests/Transport/cURL.php new file mode 100644 index 00000000000..55390adc2b5 --- /dev/null +++ b/vendor/rmccue/requests/tests/Transport/cURL.php @@ -0,0 +1,5 @@ + '100 Continue', + 101 => '101 Switching Protocols', + 200 => '200 OK', + 201 => '201 Created', + 202 => '202 Accepted', + 203 => '203 Non-Authoritative Information', + 204 => '204 No Content', + 205 => '205 Reset Content', + 206 => '206 Partial Content', + 300 => '300 Multiple Choices', + 301 => '301 Moved Permanently', + 302 => '302 Found', + 303 => '303 See Other', + 304 => '304 Not Modified', + 305 => '305 Use Proxy', + 306 => '306 (Unused)', + 307 => '307 Temporary Redirect', + 400 => '400 Bad Request', + 401 => '401 Unauthorized', + 402 => '402 Payment Required', + 403 => '403 Forbidden', + 404 => '404 Not Found', + 405 => '405 Method Not Allowed', + 406 => '406 Not Acceptable', + 407 => '407 Proxy Authentication Required', + 408 => '408 Request Timeout', + 409 => '409 Conflict', + 410 => '410 Gone', + 411 => '411 Length Required', + 412 => '412 Precondition Failed', + 413 => '413 Request Entity Too Large', + 414 => '414 Request-URI Too Long', + 415 => '415 Unsupported Media Type', + 416 => '416 Requested Range Not Satisfiable', + 417 => '417 Expectation Failed', + 500 => '500 Internal Server Error', + 501 => '501 Not Implemented', + 502 => '502 Bad Gateway', + 503 => '503 Service Unavailable', + 504 => '504 Gateway Timeout', + 505 => '505 HTTP Version Not Supported', + ); + + public function request($url, $headers = array(), $data = array(), $options = array()) { + $status = self::$messages[$this->code]; + $response = "HTTP/1.0 $status\r\n"; + $response .= "Content-Type: text/plain\r\n"; + if ($this->chunked) { + $response .= "Transfer-Encoding: chunked\r\n"; + } + $response .= $this->raw_headers; + $response .= "Connection: close\r\n\r\n"; + $response .= $this->body; + return $response; + } + + public function request_multiple($requests, $options) { + $responses = array(); + foreach ($requests as $id => $request) { + $handler = new MockTransport(); + $handler->code = $request['options']['mock.code']; + $handler->chunked = $request['options']['mock.chunked']; + $handler->body = $request['options']['mock.body']; + $handler->raw_headers = $request['options']['mock.raw_headers']; + $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); + + if (!empty($options['mock.parse'])) { + $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request)); + $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id)); + } + } + + return $responses; + } + + public static function test() { + return true; + } +} + +class RawTransport implements Requests_Transport { + public $data = ''; + public function request($url, $headers = array(), $data = array(), $options = array()) { + return $this->data; + } + public function request_multiple($requests, $options) { + foreach ($requests as $id => &$request) { + $handler = new RawTransport(); + $handler->data = $request['options']['raw.data']; + $request = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); + } + + return $requests; + } + public static function test() { + return true; + } +} diff --git a/vendor/rmccue/requests/tests/phpunit.xml.dist b/vendor/rmccue/requests/tests/phpunit.xml.dist new file mode 100644 index 00000000000..847b9b661ec --- /dev/null +++ b/vendor/rmccue/requests/tests/phpunit.xml.dist @@ -0,0 +1,35 @@ + + + + + Auth + + + Transport + + + ChunkedEncoding.php + Cookies.php + IDNAEncoder.php + IRI.php + Requests.php + Response/Headers.php + SSL.php + + + + + + + + + + . + + + ../library + + + \ No newline at end of file diff --git a/vendor/smarty/smarty/.gitattributes b/vendor/smarty/smarty/.gitattributes new file mode 100644 index 00000000000..412eeda78dc --- /dev/null +++ b/vendor/smarty/smarty/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/vendor/smarty/smarty/.gitignore b/vendor/smarty/smarty/.gitignore new file mode 100644 index 00000000000..0ff15aa4961 --- /dev/null +++ b/vendor/smarty/smarty/.gitignore @@ -0,0 +1,221 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist/ +build/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +.idea/ + +# Smarty +lexer/*.php +lexer/*.out diff --git a/vendor/smarty/smarty/.travis.yml b/vendor/smarty/smarty/.travis.yml new file mode 100644 index 00000000000..421ed9e8d90 --- /dev/null +++ b/vendor/smarty/smarty/.travis.yml @@ -0,0 +1,24 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +matrix: + allow_failures: + - php: hhvm + +before_script: + - travis_retry composer --prefer-source --dev install + +install: + - git clone --depth=50 --branch=master git://github.com/smarty-php/smarty-phpunit.git + +script: + - cd smarty-phpunit + - phpunit ./ + diff --git a/vendor/smarty/smarty/COMPOSER_RELEASE_NOTES.txt b/vendor/smarty/smarty/COMPOSER_RELEASE_NOTES.txt new file mode 100644 index 00000000000..c943d9f2ebc --- /dev/null +++ b/vendor/smarty/smarty/COMPOSER_RELEASE_NOTES.txt @@ -0,0 +1,29 @@ + + +Starting with Smarty 3.1.21 Composer has been configured to load the packages from github. + +******************************************************************************* +* * +* NOTE: Because of this change you must clear your local composer cache with * +* the "composer clearcache" command * +* * +******************************************************************************* + +To get the latest stable version use + "require": { + "smarty/smarty": "~3.1" + } +in your composer.json file. + +To get the trunk version use + "require": { + "smarty/smarty": "~3.1@dev" + } + +The "smarty/smarty" package will start at libs/.... subfolder. + +To retrieve the development and documentation folders add + "require-dev": { + "smarty/smarty-dev": "~3.1@dev" + } + diff --git a/vendor/smarty/smarty/COPYING.lib b/vendor/smarty/smarty/COPYING.lib new file mode 100644 index 00000000000..02bbb60bc49 --- /dev/null +++ b/vendor/smarty/smarty/COPYING.lib @@ -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. \ No newline at end of file diff --git a/vendor/smarty/smarty/INHERITANCE_RELEASE_NOTES.txt b/vendor/smarty/smarty/INHERITANCE_RELEASE_NOTES.txt new file mode 100644 index 00000000000..8b2c64a1bfe --- /dev/null +++ b/vendor/smarty/smarty/INHERITANCE_RELEASE_NOTES.txt @@ -0,0 +1,67 @@ +Starting with version 3.1.28 template inheritance is no longer a compile time process. +All {block} tag parent/child relations are resolved at run time. +This does resolve all known existing restrictions (see below). + +The $smarty::$inheritance_merge_compiled_includes property has been removed. +Any access to it is ignored. + +This does enable some new features: + +Any code outside root {block} tags in child templates is now executed but any output will be ignored. + + {extends 'foo.tpl'} + {$bar = 'on'} // assigns variable $bar seen in parent templates + {block 'buh'}{/block} + + {extends 'foo.tpl'} + {$bar} // the output of variable bar is ignored + {block 'buh'}{/block} + +{block} tags can be dynamically en/disabled by conditions. + + {block 'root'} + {if $foo} + {block 'v1'} + .... + {/block} + {else} + {block 'v1'} + .... + {/block} + {/if} + {/block} + + + +THE FOLLOWING RESTRICTIONS ARE NO LONGER EXISTING: +In Smarty 3.1 template inheritance is a compile time process. All the extending of {block} tags +is done at compile time and the parent and child templates are compiled in a single compiled template. +{include} subtemplate could also {block} tags. Such subtemplate could not compiled by it's own because +it could be used in other context where the {block} extended with a different result. For that reasion +the compiled code of {include} subtemplates gets also merged in compiled inheritance template. + +Merging the code into a single compile template has some drawbacks. +1. You could not use variable file names in {include} Smarty would use the {include} of compilation time. +2. You could not use individual compile_id in {include} +3. Seperate caching of subtemplate was not possible +4. Any change of the template directory structure between calls was not necessarily seen. + +Starting with 3.1.15 some of the above conditions got checked and resulted in an exception. It turned out +that a couple of users did use some of above and now got exceptions. + +To resolve this starting with 3.1.16 there is a new configuration parameter $inheritance_merge_compiled_includes. +For most backward compatibility its default setting is true. +With this setting all {include} subtemplate will be merge into the compiled inheritance template, but the above cases +could be rejected by exception. + + +If $smarty->inheritance_merge_compiled_includes = false; {include} subtemplate will not be merged. +You must now manually merge all {include} subtemplate which do contain {block} tags. This is done by setting the "inline" option. +{include file='foo.bar' inline} + +1. In case of a variable file name like {include file=$foo inline} you must use the variable in a compile_id $smarty->compile_id = $foo; +2. If you use individual compile_id in {include file='foo.tpl' compile_id=$bar inline} it must be used in the + global compile_id as well $smarty->compile_id = $bar; +3. If call templates with different template_dir configurations and a parent could same named child template from different folders + you must make the folder name part of the compile_id. + diff --git a/vendor/smarty/smarty/NEW_FEATURES.txt b/vendor/smarty/smarty/NEW_FEATURES.txt new file mode 100644 index 00000000000..1a51c71d9b9 --- /dev/null +++ b/vendor/smarty/smarty/NEW_FEATURES.txt @@ -0,0 +1,133 @@ + + +This file contains a brief description of new features which have been added to Smarty 3.1 + +Smarty 3.1.28 + + OPCACHE + ======= + Smarty does now invalidate automatically updated and cleared compiled or cached template files in OPCACHE. + Correct operation is no longer dependent on OPCACHE configuration settings. + + Template inheritance + ==================== + Template inheritance is now processed in run time. + See the INHERITANCE_RELEASE_NOTES + + Modifier regex_replace + ====================== + An optional limit parameter was added + + fetch() and display() + ===================== + The fetch() and display() methods of the template object accept now optionally the same parameter + as the corresponding Smarty methods to get the content of another template. + Example: + $template->display(); Does display template of template object + $template->display('foo.tpl'); Does display template 'foo.bar' + + File: resource + ============== + Multiple template_dir entries can now be selected by a comma separated list of indices. + The template_dir array is searched in the order of the indices. (Could be used to change the default search order) + Example: + $smarty->display('[1],[0]foo.bar'); + + Filter support + ============== + Optional filter names + An optional filter name was added to $smarty->registerFilter(). It can be used to unregister a filter by name. + - $smarty->registerFilter('output', $callback, 'name'); + $smarty->unregister('output', 'name'); + + Closures + $smarty->registerFilter() does now accept closures. + - $smarty->registerFilter('pre', function($source) {return $source;}); + If no optional filter name was specified it gets the default name 'closure'. + If you register multiple closures register each with a unique filter name. + - $smarty->registerFilter('pre', function($source) {return $source;}, 'closure_1'); + - $smarty->registerFilter('pre', function($source) {return $source;}, 'closure_2'); + + +Smarty 3.1.22 + + Namespace support within templates + ================================== + Within templates you can now use namespace specifications on: + - Constants like foo\bar\FOO + - Class names like foo\bar\Baz::FOO, foo\bar\Baz::$foo, foo\bar\Baz::foo() + - PHP function names like foo\bar\baz() + + Security + ======== + - disable special $smarty variable - + The Smarty_Security class has the new property $disabled_special_smarty_vars. + It's an array which can be loaded with the $smarty special variable names like + 'template_object', 'template', 'current_dir' and others which will be disabled. + Note: That this security check is performed at compile time. + + - limit template nesting - + Property $max_template_nesting of Smarty_Security does set the maximum template nesting level. + The main template is level 1. The nesting level is checked at run time. When the maximum will be exceeded + an Exception will be thrown. The default setting is 0 which does disable this check. + + - trusted static methods - + The Smarty_Security class has the new property $trusted_static_methods to restrict access to static methods. + It's an nested array of trusted class and method names. + Format: + array ( + 'class_1' => array('method_1', 'method_2'), // allowed methods + 'class_2' => array(), // all methods of class allowed + ) + To disable access for all methods of all classes set $trusted_static_methods = null; + The default value is an empty array() which does enables all methods of all classes, but for backward compatibility + the setting of $static_classes will be checked. + Note: That this security check is performed at compile time. + + - trusted static properties - + The Smarty_Security class has the new property $trusted_static_properties to restrict access to static properties. + It's an nested array of trusted class and property names. + Format: + array ( + 'class_1' => array('prop_1', 'prop_2'), // allowed properties listed + 'class_2' => array(), // all properties of class allowed + } + To disable access for all properties of all classes set $trusted_static_properties = null; + The default value is an empty array() which does enables all properties of all classes, but for backward compatibility + the setting of $static_classes will be checked. + Note: That this security check is performed at compile time. + + - trusted constants . + The Smarty_Security class has the new property $trusted_constants to restrict access to constants. + It's an array of trusted constant names. + Format: + array ( + 'SMARTY_DIR' , // allowed constant + } + If the array is empty (default) the usage of constants can be controlled with the + Smarty_Security::$allow_constants property (default true) + + + + Compiled Templates + ================== + Smarty does now automatically detects a change of the $merge_compiled_includes and $escape_html + property and creates different compiled templates files depending on the setting. + + Same applies to config files and the $config_overwrite, $config_booleanize and + $config_read_hidden properties. + + Debugging + ========= + The layout of the debug window has been changed for better readability + + New class constants + Smarty::DEBUG_OFF + Smarty::DEBUG_ON + Smarty::DEBUG_INDIVIDUAL + have been introduced for setting the $debugging property. + + Smarty::DEBUG_INDIVIDUAL will create for each display() and fetch() call an individual debug window. + + . + diff --git a/vendor/smarty/smarty/README b/vendor/smarty/smarty/README new file mode 100644 index 00000000000..08b397c3f69 --- /dev/null +++ b/vendor/smarty/smarty/README @@ -0,0 +1,575 @@ +Smarty 3.x + +Author: Monte Ohrt +Author: Uwe Tews + +AN INTRODUCTION TO SMARTY 3 + +NOTICE FOR 3.1 release: + +Please see the SMARTY_3.1_NOTES.txt file that comes with the distribution. + +NOTICE for 3.0.5 release: + +Smarty now follows the PHP error_reporting level by default. If PHP does not mask E_NOTICE and you try to access an unset template variable, you will now get an E_NOTICE warning. To revert to the old behavior: + +$smarty->error_reporting = E_ALL & ~E_NOTICE; + +NOTICE for 3.0 release: + +IMPORTANT: Some API adjustments have been made between the RC4 and 3.0 release. +We felt it is better to make these now instead of after a 3.0 release, then have to +immediately deprecate APIs in 3.1. Online documentation has been updated +to reflect these changes. Specifically: + +---- API CHANGES RC4 -> 3.0 ---- + +$smarty->register->* +$smarty->unregister->* +$smarty->utility->* +$samrty->cache->* + +Have all been changed to local method calls such as: + +$smarty->clearAllCache() +$smarty->registerFoo() +$smarty->unregisterFoo() +$smarty->testInstall() +etc. + +Registration of function, block, compiler, and modifier plugins have been +consolidated under two API calls: + +$smarty->registerPlugin(...) +$smarty->unregisterPlugin(...) + +Registration of pre, post, output and variable filters have been +consolidated under two API calls: + +$smarty->registerFilter(...) +$smarty->unregisterFilter(...) + +Please refer to the online documentation for all specific changes: + +http://www.smarty.net/documentation + +---- + +The Smarty 3 API has been refactored to a syntax geared +for consistency and modularity. The Smarty 2 API syntax is still supported, but +will throw a deprecation notice. You can disable the notices, but it is highly +recommended to adjust your syntax to Smarty 3, as the Smarty 2 syntax must run +through an extra rerouting wrapper. + +Basically, all Smarty methods now follow the "fooBarBaz" camel case syntax. Also, +all Smarty properties now have getters and setters. So for example, the property +$smarty->cache_dir can be set with $smarty->setCacheDir('foo/') and can be +retrieved with $smarty->getCacheDir(). + +Some of the Smarty 3 APIs have been revoked such as the "is*" methods that were +just duplicate functions of the now available "get*" methods. + +Here is a rundown of the Smarty 3 API: + +$smarty->fetch($template, $cache_id = null, $compile_id = null, $parent = null) +$smarty->display($template, $cache_id = null, $compile_id = null, $parent = null) +$smarty->isCached($template, $cache_id = null, $compile_id = null) +$smarty->createData($parent = null) +$smarty->createTemplate($template, $cache_id = null, $compile_id = null, $parent = null) +$smarty->enableSecurity() +$smarty->disableSecurity() +$smarty->setTemplateDir($template_dir) +$smarty->addTemplateDir($template_dir) +$smarty->templateExists($resource_name) +$smarty->loadPlugin($plugin_name, $check = true) +$smarty->loadFilter($type, $name) +$smarty->setExceptionHandler($handler) +$smarty->addPluginsDir($plugins_dir) +$smarty->getGlobal($varname = null) +$smarty->getRegisteredObject($name) +$smarty->getDebugTemplate() +$smarty->setDebugTemplate($tpl_name) +$smarty->assign($tpl_var, $value = null, $nocache = false) +$smarty->assignGlobal($varname, $value = null, $nocache = false) +$smarty->assignByRef($tpl_var, &$value, $nocache = false) +$smarty->append($tpl_var, $value = null, $merge = false, $nocache = false) +$smarty->appendByRef($tpl_var, &$value, $merge = false) +$smarty->clearAssign($tpl_var) +$smarty->clearAllAssign() +$smarty->configLoad($config_file, $sections = null) +$smarty->getVariable($variable, $_ptr = null, $search_parents = true, $error_enable = true) +$smarty->getConfigVariable($variable) +$smarty->getStreamVariable($variable) +$smarty->getConfigVars($varname = null) +$smarty->clearConfig($varname = null) +$smarty->getTemplateVars($varname = null, $_ptr = null, $search_parents = true) +$smarty->clearAllCache($exp_time = null, $type = null) +$smarty->clearCache($template_name, $cache_id = null, $compile_id = null, $exp_time = null, $type = null) + +$smarty->registerPlugin($type, $tag, $callback, $cacheable = true, $cache_attr = array()) + +$smarty->registerObject($object_name, $object_impl, $allowed = array(), $smarty_args = true, $block_methods = array()) + +$smarty->registerFilter($type, $function_name) +$smarty->registerResource($resource_type, $function_names) +$smarty->registerDefaultPluginHandler($function_name) +$smarty->registerDefaultTemplateHandler($function_name) + +$smarty->unregisterPlugin($type, $tag) +$smarty->unregisterObject($object_name) +$smarty->unregisterFilter($type, $function_name) +$smarty->unregisterResource($resource_type) + +$smarty->compileAllTemplates($extension = '.tpl', $force_compile = false, $time_limit = 0, $max_errors = null) +$smarty->clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) +$smarty->testInstall() + +// then all the getters/setters, available for all properties. Here are a few: + +$caching = $smarty->getCaching(); // get $smarty->caching +$smarty->setCaching(true); // set $smarty->caching +$smarty->setDeprecationNotices(false); // set $smarty->deprecation_notices +$smarty->setCacheId($id); // set $smarty->cache_id +$debugging = $smarty->getDebugging(); // get $smarty->debugging + + +FILE STRUCTURE + +The Smarty 3 file structure is similar to Smarty 2: + +/libs/ + Smarty.class.php +/libs/sysplugins/ + internal.* +/libs/plugins/ + function.mailto.php + modifier.escape.php + ... + +A lot of Smarty 3 core functionality lies in the sysplugins directory; you do +not need to change any files here. The /libs/plugins/ folder is where Smarty +plugins are located. You can add your own here, or create a separate plugin +directory, just the same as Smarty 2. You will still need to create your own +/cache/, /templates/, /templates_c/, /configs/ folders. Be sure /cache/ and +/templates_c/ are writable. + +The typical way to use Smarty 3 should also look familiar: + +require('Smarty.class.php'); +$smarty = new Smarty; +$smarty->assign('foo','bar'); +$smarty->display('index.tpl'); + + +However, Smarty 3 works completely different on the inside. Smarty 3 is mostly +backward compatible with Smarty 2, except for the following items: + +*) Smarty 3 is PHP 5 only. It will not work with PHP 4. +*) The {php} tag is disabled by default. Enable with $smarty->allow_php_tag=true. +*) Delimiters surrounded by whitespace are no longer treated as Smarty tags. + Therefore, { foo } will not compile as a tag, you must use {foo}. This change + Makes Javascript/CSS easier to work with, eliminating the need for {literal}. + This can be disabled by setting $smarty->auto_literal = false; +*) The Smarty 3 API is a bit different. Many Smarty 2 API calls are deprecated + but still work. You will want to update your calls to Smarty 3 for maximum + efficiency. + + +There are many things that are new to Smarty 3. Here are the notable items: + +LEXER/PARSER +============ + +Smarty 3 now uses a lexing tokenizer for its parser/compiler. Basically, this +means Smarty has some syntax additions that make life easier such as in-template +math, shorter/intuitive function parameter options, infinite function recursion, +more accurate error handling, etc. + + +WHAT IS NEW IN SMARTY TEMPLATE SYNTAX +===================================== + +Smarty 3 allows expressions almost anywhere. Expressions can include PHP +functions as long as they are not disabled by the security policy, object +methods and properties, etc. The {math} plugin is no longer necessary but +is still supported for BC. + +Examples: +{$x+$y} will output the sum of x and y. +{$foo = strlen($bar)} function in assignment +{assign var=foo value= $x+$y} in attributes +{$foo = myfunct( ($x+$y)*3 )} as function parameter +{$foo[$x+3]} as array index + +Smarty tags can be used as values within other tags. +Example: {$foo={counter}+3} + +Smarty tags can also be used inside double quoted strings. +Example: {$foo="this is message {counter}"} + +You can define arrays within templates. +Examples: +{assign var=foo value=[1,2,3]} +{assign var=foo value=['y'=>'yellow','b'=>'blue']} +Arrays can be nested. +{assign var=foo value=[1,[9,8],3]} + +There is a new short syntax supported for assigning variables. +Example: {$foo=$bar+2} + +You can assign a value to a specific array element. If the variable exists but +is not an array, it is converted to an array before the new values are assigned. +Examples: +{$foo['bar']=1} +{$foo['bar']['blar']=1} + +You can append values to an array. If the variable exists but is not an array, +it is converted to an array before the new values are assigned. +Example: {$foo[]=1} + +You can use a PHP-like syntax for accessing array elements, as well as the +original "dot" notation. +Examples: +{$foo[1]} normal access +{$foo['bar']} +{$foo['bar'][1]} +{$foo[$x+$x]} index may contain any expression +{$foo[$bar[1]]} nested index +{$foo[section_name]} smarty section access, not array access! + +The original "dot" notation stays, and with improvements. +Examples: +{$foo.a.b.c} => $foo['a']['b']['c'] +{$foo.a.$b.c} => $foo['a'][$b]['c'] with variable index +{$foo.a.{$b+4}.c} => $foo['a'][$b+4]['c'] with expression as index +{$foo.a.{$b.c}} => $foo['a'][$b['c']] with nested index + +note that { and } are used to address ambiguties when nesting the dot syntax. + +Variable names themselves can be variable and contain expressions. +Examples: +$foo normal variable +$foo_{$bar} variable name containing other variable +$foo_{$x+$y} variable name containing expressions +$foo_{$bar}_buh_{$blar} variable name with multiple segments +{$foo_{$x}} will output the variable $foo_1 if $x has a value of 1. + +Object method chaining is implemented. +Example: {$object->method1($x)->method2($y)} + +{for} tag added for looping (replacement for {section} tag): +{for $x=0, $y=count($foo); $x<$y; $x++} .... {/for} +Any number of statements can be used separated by comma as the first +inital expression at {for}. + +{for $x = $start to $end step $step} ... {/for}is in the SVN now . +You can use also +{for $x = $start to $end} ... {/for} +In this case the step value will be automaticall 1 or -1 depending on the start and end values. +Instead of $start and $end you can use any valid expression. +Inside the loop the following special vars can be accessed: +$x@iteration = number of iteration +$x@total = total number of iterations +$x@first = true on first iteration +$x@last = true on last iteration + + +The Smarty 2 {section} syntax is still supported. + +New shorter {foreach} syntax to loop over an array. +Example: {foreach $myarray as $var}...{/foreach} + +Within the foreach loop, properties are access via: + +$var@key foreach $var array key +$var@iteration foreach current iteration count (1,2,3...) +$var@index foreach current index count (0,1,2...) +$var@total foreach $var array total +$var@first true on first iteration +$var@last true on last iteration + +The Smarty 2 {foreach} tag syntax is still supported. + +NOTE: {$bar[foo]} still indicates a variable inside of a {section} named foo. +If you want to access an array element with index foo, you must use quotes +such as {$bar['foo']}, or use the dot syntax {$bar.foo}. + +while block tag is now implemented: +{while $foo}...{/while} +{while $x lt 10}...{/while} + +Direct access to PHP functions: +Just as you can use PHP functions as modifiers directly, you can now access +PHP functions directly, provided they are permitted by security settings: +{time()} + +There is a new {function}...{/function} block tag to implement a template function. +This enables reuse of code sequences like a plugin function. It can call itself recursively. +Template function must be called with the new {call name=foo...} tag. + +Example: + +Template file: +{function name=menu level=0} +

    + {foreach $data as $entry} + {if is_array($entry)} +
  • {$entry@key}
  • + {call name=menu data=$entry level=$level+1} + {else} +
  • {$entry}
  • + {/if} + {/foreach} +
+{/function} + +{$menu = ['item1','item2','item3' => ['item3-1','item3-2','item3-3' => + ['item3-3-1','item3-3-2']],'item4']} + +{call name=menu data=$menu} + + +Generated output: + * item1 + * item2 + * item3 + o item3-1 + o item3-2 + o item3-3 + + item3-3-1 + + item3-3-2 + * item4 + +The function tag itself must have the "name" attribute. This name is the tag +name when calling the function. The function tag may have any number of +additional attributes. These will be default settings for local variables. + +New {nocache} block function: +{nocache}...{/nocache} will declare a section of the template to be non-cached +when template caching is enabled. + +New nocache attribute: +You can declare variable/function output as non-cached with the nocache attribute. +Examples: + +{$foo nocache=true} +{$foo nocache} /* same */ + +{foo bar="baz" nocache=true} +{foo bar="baz" nocache} /* same */ + +{time() nocache=true} +{time() nocache} /* same */ + +Or you can also assign the variable in your script as nocache: +$smarty->assign('foo',$something,true); // third param is nocache setting +{$foo} /* non-cached */ + +$smarty.current_dir returns the directory name of the current template. + +You can use strings directly as templates with the "string" resource type. +Examples: +$smarty->display('string:This is my template, {$foo}!'); // php +{include file="string:This is my template, {$foo}!"} // template + + + +VARIABLE SCOPE / VARIABLE STORAGE +================================= + +In Smarty 2, all assigned variables were stored within the Smarty object. +Therefore, all variables assigned in PHP were accessible by all subsequent +fetch and display template calls. + +In Smarty 3, we have the choice to assign variables to the main Smarty object, +to user-created data objects, and to user-created template objects. +These objects can be chained. The object at the end of a chain can access all +variables belonging to that template and all variables within the parent objects. +The Smarty object can only be the root of a chain, but a chain can be isolated +from the Smarty object. + +All known Smarty assignment interfaces will work on the data and template objects. + +Besides the above mentioned objects, there is also a special storage area for +global variables. + +A Smarty data object can be created as follows: +$data = $smarty->createData(); // create root data object +$data->assign('foo','bar'); // assign variables as usual +$data->config_load('my.conf'); // load config file + +$data= $smarty->createData($smarty); // create data object having a parent link to +the Smarty object + +$data2= $smarty->createData($data); // create data object having a parent link to +the $data data object + +A template object can be created by using the createTemplate method. It has the +same parameter assignments as the fetch() or display() method. +Function definition: +function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null) + +The first parameter can be a template name, a smarty object or a data object. + +Examples: +$tpl = $smarty->createTemplate('mytpl.tpl'); // create template object not linked to any parent +$tpl->assign('foo','bar'); // directly assign variables +$tpl->config_load('my.conf'); // load config file + +$tpl = $smarty->createTemplate('mytpl.tpl',$smarty); // create template having a parent link to the Smarty object +$tpl = $smarty->createTemplate('mytpl.tpl',$data); // create template having a parent link to the $data object + +The standard fetch() and display() methods will implicitly create a template object. +If the $parent parameter is not specified in these method calls, the template object +is will link back to the Smarty object as it's parent. + +If a template is called by an {include...} tag from another template, the +subtemplate links back to the calling template as it's parent. + +All variables assigned locally or from a parent template are accessible. If the +template creates or modifies a variable by using the {assign var=foo...} or +{$foo=...} tags, these new values are only known locally (local scope). When the +template exits, none of the new variables or modifications can be seen in the +parent template(s). This is same behavior as in Smarty 2. + +With Smarty 3, we can assign variables with a scope attribute which allows the +availablility of these new variables or modifications globally (ie in the parent +templates.) + +Possible scopes are local, parent, root and global. +Examples: +{assign var=foo value='bar'} // no scope is specified, the default 'local' +{$foo='bar'} // same, local scope +{assign var=foo value='bar' scope='local'} // same, local scope + +{assign var=foo value='bar' scope='parent'} // Values will be available to the parent object +{$foo='bar' scope='parent'} // (normally the calling template) + +{assign var=foo value='bar' scope='root'} // Values will be exported up to the root object, so they can +{$foo='bar' scope='root'} // be seen from all templates using the same root. + +{assign var=foo value='bar' scope='global'} // Values will be exported to global variable storage, +{$foo='bar' scope='global'} // they are available to any and all templates. + + +The scope attribute can also be attached to the {include...} tag. In this case, +the specified scope will be the default scope for all assignments within the +included template. + + +PLUGINS +======= + +Smarty 3 plugins follow the same coding rules as in Smarty 2. +The main difference is that the template object is now passed in place of the smarty object. +The smarty object can be still be accessed through $template->smarty. + +smarty_plugintype_name (array $params, Smarty_Internal_Template $template) + +The Smarty 2 plugins are still compatible as long as they do not make use of specific Smarty 2 internals. + + +TEMPLATE INHERITANCE: +===================== + +With template inheritance you can define blocks, which are areas that can be +overriden by child templates, so your templates could look like this: + +parent.tpl: + + + {block name='title'}My site name{/block} + + +

{block name='page-title'}Default page title{/block}

+
+ {block name='content'} + Default content + {/block} +
+ + + +child.tpl: +{extends file='parent.tpl'} +{block name='title'} +Child title +{/block} + +grandchild.tpl: +{extends file='child.tpl'} +{block name='title'}Home - {$smarty.block.parent}{/block} +{block name='page-title'}My home{/block} +{block name='content'} + {foreach $images as $img} + {$img.description} + {/foreach} +{/block} + +We redefined all the blocks here, however in the title block we used {$smarty.block.parent}, +which tells Smarty to insert the default content from the parent template in its place. +The content block was overriden to display the image files, and page-title has also be +overriden to display a completely different title. + +If we render grandchild.tpl we will get this: + + + Home - Child title + + +

My home

+
+ image + image + image +
+ + + +NOTE: In the child templates everything outside the {extends} or {block} tag sections +is ignored. + +The inheritance tree can be as big as you want (meaning you can extend a file that +extends another one that extends another one and so on..), but be aware that all files +have to be checked for modifications at runtime so the more inheritance the more overhead you add. + +Instead of defining the parent/child relationships with the {extends} tag in the child template you +can use the resource as follow: + +$smarty->display('extends:parent.tpl|child.tpl|grandchild.tpl'); + +Child {block} tags may optionally have a append or prepend attribute. In this case the parent block content +is appended or prepended to the child block content. + +{block name='title' append} My title {/block} + + +PHP STREAMS: +============ + +(see online documentation) + +VARIBLE FILTERS: +================ + +(see online documentation) + + +STATIC CLASS ACCESS AND NAMESPACE SUPPORT +========================================= + +You can register a class with optional namespace for the use in the template like: + +$smarty->register->templateClass('foo','name\name2\myclass'); + +In the template you can use it like this: +{foo::method()} etc. + + +======================= + +Please look through it and send any questions/suggestions/etc to the forums. + +http://www.phpinsider.com/smarty-forum/viewtopic.php?t=14168 + +Monte and Uwe diff --git a/vendor/smarty/smarty/README.md b/vendor/smarty/smarty/README.md new file mode 100644 index 00000000000..5783eb3e05f --- /dev/null +++ b/vendor/smarty/smarty/README.md @@ -0,0 +1,65 @@ +#Smarty 3 template engine +##Distribution repository + +> Smarty 3.1.28 introduces run time template inheritance + +> Read the NEW_FEATURES and INHERITANCE_RELEASE_NOTES file for recent extensions to Smarty 3.1 functionality + +Smarty versions 3.1.11 or later are now on github and can be installed with Composer. + + +The "smarty/smarty" package will start at libs/.... subfolder. + +To get the latest stable version of Smarty 3.1 use + +```json +"require": { + "smarty/smarty": "~3.1" +} +``` + +in your composer.json file. + +To get the trunk version use + +```json +"require": { + "smarty/smarty": "~3.1@dev" +} +``` + +For a specific version use something like + +```json +"require": { + "smarty/smarty": "3.1.19" +} +``` + +PHPUnit test can be installed by corresponding composer entries like + +```json +"require": { + "smarty/smarty-phpunit": "3.1.19" +} +``` + +Similar applies for the lexer/parser generator + +```json +"require": { + "smarty/smarty-lexer": "3.1.19" +} +``` + +Or you could use + +```json +"require": { + "smarty/smarty-dev": "3.1.19" +} +``` + +Which is a wrapper to install all 3 packages + +Composer can also be used for Smarty2 versions 2.6.24 to 2.6.28 diff --git a/vendor/smarty/smarty/SMARTY_2_BC_NOTES.txt b/vendor/smarty/smarty/SMARTY_2_BC_NOTES.txt new file mode 100644 index 00000000000..79a2cb1b663 --- /dev/null +++ b/vendor/smarty/smarty/SMARTY_2_BC_NOTES.txt @@ -0,0 +1,109 @@ += Known incompatibilities with Smarty 2 = + +== Syntax == + +Smarty 3 API has a new syntax. Much of the Smarty 2 syntax is supported +by a wrapper but deprecated. See the README that comes with Smarty 3 for more +information. + +The {$array|@mod} syntax has always been a bit confusing, where an "@" is required +to apply a modifier to an array instead of the individual elements. Normally you +always want the modifier to apply to the variable regardless of its type. In Smarty 3, +{$array|mod} and {$array|@mod} behave identical. It is safe to drop the "@" and the +modifier will still apply to the array. If you really want the modifier to apply to +each array element, you must loop the array in-template, or use a custom modifier that +supports array iteration. Most smarty functions already escape values where necessary +such as {html_options} + +== PHP Version == +Smarty 3 is PHP 5 only. It will not work with PHP 4. + +== {php} Tag == +The {php} tag is disabled by default. The use of {php} tags is +deprecated. It can be enabled with $smarty->allow_php_tag=true. + +But if you scatter PHP code which belongs together into several +{php} tags it may not work any longer. + +== Delimiters and whitespace == +Delimiters surrounded by whitespace are no longer treated as Smarty tags. +Therefore, { foo } will not compile as a tag, you must use {foo}. This change +Makes Javascript/CSS easier to work with, eliminating the need for {literal}. +This can be disabled by setting $smarty->auto_literal = false; + +== Unquoted Strings == +Smarty 2 was a bit more forgiving (and ambiguous) when it comes to unquoted strings +in parameters. Smarty3 is more restrictive. You can still pass strings without quotes +so long as they contain no special characters. (anything outside of A-Za-z0-9_) + +For example filename strings must be quoted + +{include file='path/foo.tpl'} + + +== Extending the Smarty class == +Smarty 3 makes use of the __construct method for initialization. If you are extending +the Smarty class, its constructor is not called implicitly if the your child class defines +its own constructor. In order to run Smarty's constructor, a call to parent::__construct() +within your child constructor is required. + + +class MySmarty extends Smarty { + function __construct() { + parent::__construct(); + + // your initialization code goes here + + } +} + + +== Autoloader == +Smarty 3 does register its own autoloader with spl_autoload_register. If your code has +an existing __autoload function then this function must be explicitly registered on +the __autoload stack. See http://us3.php.net/manual/en/function.spl-autoload-register.php +for further details. + +== Plugin Filenames == +Smarty 3 optionally supports the PHP spl_autoloader. The autoloader requires filenames +to be lower case. Because of this, Smarty plugin file names must also be lowercase. +In Smarty 2, mixed case file names did work. + +== Scope of Special Smarty Variables == +In Smarty 2 the special Smarty variables $smarty.section... and $smarty.foreach... +had global scope. If you had loops with the same name in subtemplates you could accidentally +overwrite values of parent template. + +In Smarty 3 these special Smarty variable have only local scope in the template which +is defining the loop. If you need their value in a subtemplate you have to pass them +as parameter. + +{include file='path/foo.tpl' index=$smarty.section.foo.index} + + +== SMARTY_RESOURCE_CHAR_SET == +Smarty 3 sets the constant SMARTY_RESOURCE_CHAR_SET to utf-8 as default template charset. +This is now used also on modifiers like escape as default charset. If your templates use +other charsets make sure that you define the constant accordingly. Otherwise you may not +get any output. + +== newline at {if} tags == +A \n was added to the compiled code of the {if},{else},{elseif},{/if} tags to get output of newlines as expected by the template source. +If one of the {if} tags is at the line end you will now get a newline in the HTML output. + +== trigger_error() == +The API function trigger_error() has been removed because it did just map to PHP trigger_error. +However it's still included in the Smarty2 API wrapper. + +== Smarty constants == +The constants +SMARTY_PHP_PASSTHRU +SMARTY_PHP_QUOTE +SMARTY_PHP_REMOVE +SMARTY_PHP_ALLOW +have been replaced with class constants +Smarty::PHP_PASSTHRU +Smarty::PHP_QUOTE +Smarty::PHP_REMOVE +Smarty::PHP_ALLOW + diff --git a/vendor/smarty/smarty/SMARTY_3.0_BC_NOTES.txt b/vendor/smarty/smarty/SMARTY_3.0_BC_NOTES.txt new file mode 100644 index 00000000000..fd8b540c26b --- /dev/null +++ b/vendor/smarty/smarty/SMARTY_3.0_BC_NOTES.txt @@ -0,0 +1,24 @@ +== Smarty2 backward compatibility == +All Smarty2 specific API functions and deprecated functionallity has been moved +to the SmartyBC class. + +== {php} Tag == +The {php} tag is no longer available in the standard Smarty calls. +The use of {php} tags is deprecated and only available in the SmartyBC class. + +== {include_php} Tag == +The {include_php} tag is no longer available in the standard Smarty calls. +The use of {include_php} tags is deprecated and only available in the SmartyBC class. + +== php template resource == +The support of the php template resource is removed. + +== $cache_dir, $compile_dir, $config_dir, $template_dir access == +The mentioned properties can't be accessed directly any longer. You must use +corresponding getter/setters like addConfigDir(), setConfigDir(), getConfigDir() + +== obsolete Smarty class properties == +The following no longer used properties are removed: +$allow_php_tag +$allow_php_template +$deprecation_notices \ No newline at end of file diff --git a/vendor/smarty/smarty/SMARTY_3.1_NOTES.txt b/vendor/smarty/smarty/SMARTY_3.1_NOTES.txt new file mode 100644 index 00000000000..57709f0d722 --- /dev/null +++ b/vendor/smarty/smarty/SMARTY_3.1_NOTES.txt @@ -0,0 +1,306 @@ +Smarty 3.1 Notes +================ + +Smarty 3.1 is a departure from 2.0 compatibility. Most notably, all +backward compatibility has been moved to a separate class file named +SmartyBC.class.php. If you require compatibility with 2.0, you will +need to use this class. + +Some differences from 3.0 are also present. 3.1 begins the journey of +requiring setters/getters for property access. So far this is only +implemented on the five directory properties: template_dir, +plugins_dir, configs_dir, compile_dir and cache_dir. These properties +are now protected, it is required to use the setters/getters instead. +That said, direct property access will still work, however slightly +slower since they will now fall through __set() and __get() and in +turn passed through the setter/getter methods. 3.2 will exhibit a full +list of setter/getter methods for all (currently) public properties, +so code-completion in your IDE will work as expected. + +There is absolutely no PHP allowed in templates any more. All +deprecated features of Smarty 2.0 are gone. Again, use the SmartyBC +class if you need any backward compatibility. + +Internal Changes + + Full UTF-8 Compatibility + +The plugins shipped with Smarty 3.1 have been rewritten to fully +support UTF-8 strings if Multibyte String is available. Without +MBString UTF-8 cannot be handled properly. For those rare cases where +templates themselves have to juggle encodings, the new modifiers +to_charset and from_charset may come in handy. + + Plugin API and Performance + +All Plugins (modifiers, functions, blocks, resources, +default_template_handlers, etc) are now receiving the +Smarty_Internal_Template instance, where they were supplied with the +Smarty instance in Smarty 3.0. *. As The Smarty_Internal_Template +mimics the behavior of Smarty, this API simplification should not +require any changes to custom plugins. + +The plugins shipped with Smarty 3.1 have been rewritten for better +performance. Most notably {html_select_date} and {html_select_time} +have been improved vastly. Performance aside, plugins have also been +reviewed and generalized in their API. {html_select_date} and +{html_select_time} now share almost all available options. + +The escape modifier now knows the $double_encode option, which will +prevent entities from being encoded again. + +The capitalize modifier now know the $lc_rest option, which makes sure +all letters following a captial letter are lower-cased. + +The count_sentences modifier now accepts (.?!) as +legitimate endings of a sentence - previously only (.) was +accepted + +The new unescape modifier is there to reverse the effects of the +escape modifier. This applies to the escape formats html, htmlall and +entity. + + default_template_handler_func + +The invocation of $smarty->$default_template_handler_func had to be +altered. Instead of a Smarty_Internal_Template, the fifth argument is +now provided with the Smarty instance. New footprint: + + +/** + * Default Template Handler + * + * called when Smarty's file: resource is unable to load a requested file + * + * @param string $type resource type (e.g. "file", "string", "eval", "resource") + * @param string $name resource name (e.g. "foo/bar.tpl") + * @param string &$content template's content + * @param integer &$modified template's modification time + * @param Smarty $smarty Smarty instance + * @return string|boolean path to file or boolean true if $content and $modified + * have been filled, boolean false if no default template + * could be loaded + */ +function default_template_handler_func($type, $name, &$content, &$modified, Smarty $smarty) { + if (false) { + // return corrected filepath + return "/tmp/some/foobar.tpl"; + } elseif (false) { + // return a template directly + $content = "the template source"; + $modified = time(); + return true; + } else { + // tell smarty that we failed + return false; + } +} + + Stuff done to the compiler + +Many performance improvements have happened internally. One notable +improvement is that all compiled templates are now handled as PHP +functions. This speeds up repeated templates tremendously, as each one +calls an (in-memory) PHP function instead of performing another file +include/scan. + +New Features + + Template syntax + + {block}..{/block} + +The {block} tag has a new hide option flag. It does suppress the block +content if no corresponding child block exists. +EXAMPLE: +parent.tpl +{block name=body hide} child content "{$smarty.block.child}" was +inserted {block} +In the above example the whole block will be suppressed if no child +block "body" is existing. + + {setfilter}..{/setfilter} + +The new {setfilter} block tag allows the definition of filters which +run on variable output. +SYNTAX: +{setfilter filter1|filter2|filter3....} +Smarty3 will lookup up matching filters in the following search order: +1. varibale filter plugin in plugins_dir. +2. a valid modifier. A modifier specification will also accept +additional parameter like filter2:'foo' +3. a PHP function +{/setfilter} will turn previous filter setting off again. +{setfilter} tags can be nested. +EXAMPLE: +{setfilter filter1} + {$foo} + {setfilter filter2} + {$bar} + {/setfilter} + {$buh} +{/setfilter} +{$blar} +In the above example filter1 will run on the output of $foo, filter2 +on $bar, filter1 again on $buh and no filter on $blar. +NOTES: +- {$foo nofilter} will suppress the filters +- These filters will run in addition to filters defined by +registerFilter('variable',...), autoLoadFilter('variable',...) and +defined default modifier. +- {setfilter} will effect only the current template, not included +subtemplates. + + Resource API + +Smarty 3.1 features a new approach to resource management. The +Smarty_Resource API allows simple, yet powerful integration of custom +resources for templates and configuration files. It offers simple +functions for loading data from a custom resource (e.g. database) as +well as define new template types adhering to the special +non-compiling (e,g, plain php) and non-compile-caching (e.g. eval: +resource type) resources. + +See demo/plugins/resource.mysql.php for an example custom database +resource. + +Note that old-fashioned registration of callbacks for resource +management has been deprecated but is still possible with SmartyBC. + + CacheResource API + +In line with the Resource API, the CacheResource API offers a more +comfortable handling of output-cache data. With the +Smarty_CacheResource_Custom accessing databases is made simple. With +the introduction of Smarty_CacheResource_KeyValueStore the +implementation of resources like memcache or APC became a no-brainer; +simple hash-based storage systems are now supporting hierarchical +output-caches. + +See demo/plugins/cacheresource.mysql.php for an example custom +database CacheResource. +See demo/plugins/cacheresource.memcache.php for an example custom +memcache CacheResource using the KeyValueStore helper. + +Note that old-fashioned registration of $cache_handler is not possible +anymore. As the functionality had not been ported to Smarty 3.0.x +properly, it has been dropped from 3.1 completely. + +Locking facilities have been implemented to avoid concurrent cache +generation. Enable cache locking by setting +$smarty->cache_locking = true; + + Relative Paths in Templates (File-Resource) + +As of Smarty 3.1 {include file="../foo.tpl"} and {include +file="./foo.tpl"} will resolve relative to the template they're in. +Relative paths are available with {include file="..."} and +{extends file="..."}. As $smarty->fetch('../foo.tpl') and +$smarty->fetch('./foo.tpl') cannot be relative to a template, an +exception is thrown. + + Addressing a specific $template_dir + +Smarty 3.1 introduces the $template_dir index notation. +$smarty->fetch('[foo]bar.tpl') and {include file="[foo]bar.tpl"} +require the template bar.tpl to be loaded from $template_dir['foo']; +Smarty::setTemplateDir() and Smarty::addTemplateDir() offer ways to +define indexes along with the actual directories. + + Mixing Resources in extends-Resource + +Taking the php extends: template resource one step further, it is now +possible to mix resources within an extends: call like +$smarty->fetch("extends:file:foo.tpl|db:bar.tpl"); + +To make eval: and string: resources available to the inheritance +chain, eval:base64:TPL_STRING and eval:urlencode:TPL_STRING have been +introduced. Supplying the base64 or urlencode flags will trigger +decoding the TPL_STRING in with either base64_decode() or urldecode(). + + extends-Resource in template inheritance + +Template based inheritance may now inherit from php's extends: +resource like {extends file="extends:foo.tpl|db:bar.tpl"}. + + New Smarty property escape_html + +$smarty->escape_html = true will autoescape all template variable +output by calling htmlspecialchars({$output}, ENT_QUOTES, +SMARTY_RESOURCE_CHAR_SET). +NOTE: +This is a compile time option. If you change the setting you must make +sure that the templates get recompiled. + + New option at Smarty property compile_check + +The automatic recompilation of modified templates can now be +controlled by the following settings: +$smarty->compile_check = COMPILECHECK_OFF (false) - template files +will not be checked +$smarty->compile_check = COMPILECHECK_ON (true) - template files will +always be checked +$smarty->compile_check = COMPILECHECK_CACHEMISS - template files will +be checked if caching is enabled and there is no existing cache file +or it has expired + + Automatic recompilation on Smarty version change + +Templates will now be automatically recompiled on Smarty version +changes to avoide incompatibillities in the compiled code. Compiled +template checked against the current setting of the SMARTY_VERSION +constant. + + default_config_handler_func() + +Analogous to the default_template_handler_func() +default_config_handler_func() has been introduced. + + default_plugin_handler_func() + +An optional default_plugin_handler_func() can be defined which gets called +by the compiler on tags which can't be resolved internally or by plugins. +The default_plugin_handler() can map tags to plugins on the fly. + +New getters/setters + +The following setters/getters will be part of the official +documentation, and will be strongly recommended. Direct property +access will still work for the foreseeable future... it will be +transparently routed through the setters/getters, and consequently a +bit slower. + +array|string getTemplateDir( [string $index] ) +replaces $smarty->template_dir; and $smarty->template_dir[$index]; +Smarty setTemplateDir( array|string $path ) +replaces $smarty->template_dir = "foo"; and $smarty->template_dir = +array("foo", "bar"); +Smarty addTemplateDir( array|string $path, [string $index]) +replaces $smarty->template_dir[] = "bar"; and +$smarty->template_dir[$index] = "bar"; + +array|string getConfigDir( [string $index] ) +replaces $smarty->config_dir; and $smarty->config_dir[$index]; +Smarty setConfigDir( array|string $path ) +replaces $smarty->config_dir = "foo"; and $smarty->config_dir = +array("foo", "bar"); +Smarty addConfigDir( array|string $path, [string $index]) +replaces $smarty->config_dir[] = "bar"; and +$smarty->config_dir[$index] = "bar"; + +array getPluginsDir() +replaces $smarty->plugins_dir; +Smarty setPluginsDir( array|string $path ) +replaces $smarty->plugins_dir = "foo"; +Smarty addPluginsDir( array|string $path ) +replaces $smarty->plugins_dir[] = "bar"; + +string getCompileDir() +replaces $smarty->compile_dir; +Smarty setCompileDir( string $path ) +replaces $smarty->compile_dir = "foo"; + +string getCacheDir() +replaces $smarty->cache_dir; +Smarty setCacheDir( string $path ) +replaces $smarty->cache_dir; diff --git a/vendor/smarty/smarty/change_log.txt b/vendor/smarty/smarty/change_log.txt new file mode 100644 index 00000000000..dadc5d17d00 --- /dev/null +++ b/vendor/smarty/smarty/change_log.txt @@ -0,0 +1,2864 @@ + ===== 3.1.29 ===== (21.12.2015) + 21.12.2015 + - optimization improve speed of filetime checks on extends and extendsall resource + + 20.12.2015 + - bugfix failure when the default resource type was set to 'extendsall' https://github.com/smarty-php/smarty/issues/123 + - update compilation of Smarty special variables + - bugfix add addition check for OS type on normalizaition of file path https://github.com/smarty-php/smarty/issues/134 + - bugfix the source uid of the extendsall resource must contain $template_dir settings https://github.com/smarty-php/smarty/issues/123 + + 19.12.2015 + - bugfix using $smarty.capture.foo in expressions could fail https://github.com/smarty-php/smarty/pull/138 + - bugfix broken PHP 5.2 compatibility https://github.com/smarty-php/smarty/issues/139 + - remove no longer used code + - improvement make sure that compiled and cache templates never can contain a trailing '?>? + + 18.12.2015 + - bugfix regression when modifier parameter was follow by math https://github.com/smarty-php/smarty/issues/132 + + 17.12.2015 + - bugfix {$smarty.capture.nameFail} did lowercase capture name https://github.com/smarty-php/smarty/issues/135 + - bugfix using {block append/prepend} on same block in multiple levels of inheritance templates could fail (forum topic 25827) + - bugfix text content consisting of just a single '0' like in {if true}0{/if} was suppressed (forum topic 25834) + + 16.12.2015 + - bugfix {foreach} did fail if from atrribute is a Generator class https://github.com/smarty-php/smarty/issues/128 + - bugfix direct access $smarty->template_dir = 'foo'; should call Smarty::setTemplateDir() https://github.com/smarty-php/smarty/issues/121 + + 15.12.2015 + - bugfix {$smarty.cookies.foo} did return the $_COOKIE array not the 'foo' value https://github.com/smarty-php/smarty/issues/122 + - bugfix a call to clearAllCache() and other should clear all internal template object caches (forum topic 25828) + + 14.12.2015 + - bugfix {$smarty.config.foo} broken in 3.1.28 https://github.com/smarty-php/smarty/issues/120 + - bugfix multiple calls of {section} with same name droped E_NOTICE error https://github.com/smarty-php/smarty/issues/118 + + ===== 3.1.28 ===== (13.12.2015) + 13.12.2015 + - bugfix {foreach} and {section} with uppercase characters in name attribute did not work (forum topic 25819) + - bugfix $smarty->debugging_ctrl = 'URL' did not work (forum topic 25811) + - bugfix Debug Console could display incorrect data when using subtemplates + + 09.12.2015 + - bugfix Smarty did fail under PHP 7.0.0 with use_include_path = true; + + 09.12.2015 + - bugfix {strip} should exclude some html tags from stripping, related to fix for https://github.com/smarty-php/smarty/issues/111 + + 08.12.2015 + - bugfix internal template function data got stored in wrong compiled file https://github.com/smarty-php/smarty/issues/114 + + 05.12.2015 + -bugfix {strip} should insert a single space https://github.com/smarty-php/smarty/issues/111 + + 25.11.2015 + -bugfix a left delimter like '[%' did fail on [%$var_[%$variable%]%] (forum topic 25798) + + 02.11.2015 + - bugfix {include} with variable file name like {include file="foo_`$bar`.tpl"} did fail in 3.1.28-dev https://github.com/smarty-php/smarty/issues/102 + + 01.11.2015 + - update config file processing + + 31.10.2015 + - bugfix add missing $trusted_dir property to SmartyBC class (forum topic 25751) + + 29.10.2015 + - improve template scope handling + + 24.10.2015 + - more optimizations of template processing + - bugfix Error when using {include} within {capture} https://github.com/smarty-php/smarty/issues/100 + + 21.10.2015 + - move some code into runtime extensions + + 18.10.2015 + - optimize filepath normalization + - rework of template inheritance + - speed and size optimizations + - bugfix under HHVM temporary cache file must only be created when caches template was updated + - fix compiled code for new {block} assign attribute + - update code generated by template function call handler + + 18.09.2015 + - bugfix {if $foo instanceof $bar} failed to compile if 2nd value is a variable https://github.com/smarty-php/smarty/issues/92 + + 17.09.2015 + - bugfix {foreach} first attribute was not correctly reset since commit 05a8fa2 of 02.08.2015 https://github.com/smarty-php/smarty/issues/90 + + 16.09.2015 + - update compiler by moving no longer needed properties, code optimizations and other + + 14.09.2015 + - optimize autoloader + - optimize subtemplate handling + - update template inheritance processing + - move code of {call} processing back into Smarty_Internal_Template class + - improvement invalidate OPCACHE for cleared compiled and cached template files (forum topic 25557) + - bugfix unintended multiple debug windows (forum topic 25699) + + 30.08.2015 + - size optimization move some runtime functions into extension + - optimize inline template processing + - optimization merge inheritance child and parent templates into one compiled template file + + 29.08.2015 + - improvement convert template inheritance into runtime processing + - bugfix {$smarty.block.parent} did always reference the root parent block https://github.com/smarty-php/smarty/issues/68 + + 23.08.2015 + - introduce Smarty::$resource_cache_mode and cache template object of {include} inside loop + - load seldom used Smarty API methods dynamically to reduce memory footprint + - cache template object of {include} if same template is included several times + - convert debug console processing to object + - use output buffers for better performance and less memory usage + - optimize nocache hash processing + - remove not really needed properties + - optimize rendering + - move caching to Smarty::_cache + - remove properties with redundant content + - optimize Smarty::templateExists() + - optimize use_include_path processing + - relocate properties for size optimization + - remove redundant code + - bugfix compiling super globals like {$smarty.get.foo} did fail in the master branch https://github.com/smarty-php/smarty/issues/77 + + 06.08.2015 + - avoid possible circular object references caused by parser/lexer objects + - rewrite compileAll... utility methods + - commit several internal improvements + - bugfix Smarty failed when compile_id did contain "|" + + 03.08.2015 + - rework clear cache methods + - bugfix compileAllConfig() was broken since 3.1.22 because of the changes in config file processing + - improve getIncludePath() to return directory if no file was given + + 02.08.2015 + - optimization and code cleanup of {foreach} and {section} compiler + - rework {capture} compiler + + 01.08.2015 + - update DateTime object can be instance of DateTimeImmutable since PHP5.5 https://github.com/smarty-php/smarty/pull/75 + - improvement show resource type and start of template source instead of uid on eval: and string: resource (forum topic 25630) + + 31.07.2015 + - optimize {foreach} and {section} compiler + + 29.07.2015 + - optimize {section} compiler for speed and size of compiled code + + 28.07.2015 + - update for PHP 7 compatibility + + 26.07.2015 + - improvement impement workaround for HHVM PHP incompatibillity https://github.com/facebook/hhvm/issues/4797 + + 25.07.2015 + - bugfix parser did hang on text starting fetch('foo.tpl') https://github.com/smarty-php/smarty/issues/70 + - improvement Added $limit parameter to regex_replace modifier #71 + - new feature multiple indices on file: resource + + 06.07.2015 + - optimize {block} compilation + - optimization get rid of __get and __set in source object + + 01.07.2015 + - optimize compile check handling + - update {foreach} compiler + - bugfix debugging console did not display string values containing \n, \r or \t correctly https://github.com/smarty-php/smarty/issues/66 + - optimize source resources + + 28.06.2015 + - move $smarty->enableSecurity() into Smarty_Security class + - optimize security isTrustedResourceDir() + - move auto load filter methods into extension + - move $smarty->getTemplateVars() into extension + - move getStreamVariable() into extension + - move $smarty->append() and $smarty->appendByRef() into extension + - optimize autoloader + - optimize file path normalization + - bugfix PATH_SEPARATOR was replaced by mistake in autoloader + - remove redundant code + + 27.06.2015 + - bugfix resolve naming conflict between custom Smarty delimiter '<%' and PHP ASP tags https://github.com/smarty-php/smarty/issues/64 + - update $smarty->_realpath for relative path not starting with './' + - update Smarty security with new realpath handling + - update {include_php} with new realpath handling + - move $smarty->loadPlugin() into extension + - minor compiler optimizations + - bugfix allow function plugins with name ending with 'close' https://github.com/smarty-php/smarty/issues/52 + - rework of $smarty->clearCompiledTemplate() and move it to its own extension + + 19.06.2015 + - improvement allow closures as callback at $smarty->registerFilter() https://github.com/smarty-php/smarty/issues/59 + + ===== 3.1.27===== (18.06.2015) + 18.06.2015 + - bugfix another update on file path normalization failed on path containing something like "/.foo/" https://github.com/smarty-php/smarty/issues/56 + + ===== 3.1.26===== (18.06.2015) + 18.06.2015 + - bugfix file path normalization failed on path containing something like "/.foo/" https://github.com/smarty-php/smarty/issues/56 + + 17.06.2015 + - bugfix calling a plugin with nocache option but no other attributes like {foo nocache} caused call to undefined function https://github.com/smarty-php/smarty/issues/55 + + ===== 3.1.25===== (15.06.2015) + 15.06.2015 + - optimization of smarty_cachereource_keyvaluestore.php code + + 14.06.2015 + - bugfix a relative sub template path could fail if template_dir path did contain /../ https://github.com/smarty-php/smarty/issues/50 + - optimization rework of path normalization + - bugfix an output tag with variable, modifier followed by an operator like {$foo|modifier+1} did fail https://github.com/smarty-php/smarty/issues/53 + + 13.06.2015 + - bugfix a custom cache resource using smarty_cachereource_keyvaluestore.php did fail if php.ini mbstring.func_overload = 2 (forum topic 25568) + + 11.06.2015 + - bugfix the lexer could hang on very large quoted strings (forum topic 25570) + + 08.06.2015 + - bugfix using {$foo} as array index like $bar.{$foo} or in double quoted string like "some {$foo} thing" failed https://github.com/smarty-php/smarty/issues/49 + + 04.06.2015 + - bugfix possible error message on unset() while compiling {block} tags https://github.com/smarty-php/smarty/issues/46 + + 01.06.2015 + - bugfix including template variables broken since 3.1.22 https://github.com/smarty-php/smarty/issues/47 + + 27.05.2015 + - bugfix {include} with variable file name must not create by default individual cache file (since 3.1.22) https://github.com/smarty-php/smarty/issues/43 + + 24.05.2015 + - bugfix if condition string 'neq' broken due to a typo https://github.com/smarty-php/smarty/issues/42 + + ===== 3.1.24===== (23.05.2015) + 23.05.2015 + - improvement on php_handling to allow very large PHP sections, better error handling + - improvement allow extreme large comment sections (forum 25538) + + 21.05.2015 + - bugfix broken PHP 5.2 compatibility when compiling 1 did compile into wrong code https://github.com/smarty-php/smarty/issues/41 + + 19.05.2015 + - bugfix compiler did overwrite existing variable value when setting the nocache attribute https://github.com/smarty-php/smarty/issues/39 + - bugfix output filter trimwhitespace could run into the pcre.backtrack_limit on large output (code.google issue 220) + - bugfix compiler could run into the pcre.backtrack_limit on larger comment or {php} tag sections (forum 25538) + + 18.05.2015 + - improvement introduce shortcuts in lexer/parser rules for most frequent terms for higher + compilation speed + + 16.05.2015 + - bugfix {php}{/php} did work just for single lines https://github.com/smarty-php/smarty/issues/33 + - improvement remove not needed ?> handling from parser to new compiler module + + 05.05.2015 + - bugfix code could be messed up when {tags} are used in multiple attributes https://github.com/smarty-php/smarty/issues/23 + + 04.05.2015 + - bugfix Smarty_Resource::parseResourceName incompatible with Google AppEngine (https://github.com/smarty-php/smarty/issues/22) + - improvement use is_file() checks to avoid errors suppressed by @ which could still cause problems (https://github.com/smarty-php/smarty/issues/24) + + 28.04.2015 + - bugfix plugins of merged subtemplates not loaded in 3.1.22-dev (forum topic 25508) 2nd fix + + 28.04.2015 + - bugfix plugins of merged subtemplates not loaded in 3.1.22-dev (forum topic 25508) + + 23.04.2015 + - bugfix a nocache template variable used as parameter at {insert} was by mistake cached + + 20.04.2015 + - bugfix at a template function containing nocache code a parmeter could overwrite a template variable of same name + + 27.03.2015 + - bugfix Smarty_Security->allow_constants=false; did also disable true, false and null (change of 16.03.2015) + - improvement added a whitelist for trusted constants to security Smarty_Security::$trusted_constants (forum topic 25471) + + 20.03.2015 + - bugfix make sure that function properties get saved only in compiled files containing the fuction definition {forum topic 25452} + - bugfix correct update of global variable values on exit of template functions. (reported under Smarty Developers) + + 16.03.2015 + - bugfix problems with {function}{/function} and {call} tags in different subtemplate cache files {forum topic 25452} + - bugfix Smarty_Security->allow_constants=false; did not disallow direct usage of defined constants like {SMARTY_DIR} {forum topic 25457} + - bugfix {block}{/block} tags did not work inside double quoted strings https://github.com/smarty-php/smarty/issues/18 + + + 15.03.2015 + - bugfix $smarty->compile_check must be restored before rendering of a just updated cache file {forum 25452} + + 14.03.2015 + - bugfix {nocache} {/nocache} tags corrupted code when used within a nocache section caused by a nocache template variable. + + - bugfix template functions defined with {function} in an included subtemplate could not be called in nocache + mode with {call... nocache} if the subtemplate had it's own cache file {forum 25452} + + 10.03.2015 + - bugfix {include ... nocache} whith variable file or compile_id attribute was not executed in nocache mode. + + 12.02.2015 + - bugfix multiple Smarty::fetch() of same template when $smarty->merge_compiled_includes = true; could cause function already defined error + + 11.02.2015 + - bugfix recursive {includes} did create E_NOTICE message when $smarty->merge_compiled_includes = true; (github issue #16) + + 22.01.2015 + - new feature security can now control access to static methods and properties + see also NEW_FEATURES.txt + + 21.01.2015 + - bugfix clearCompiledTemplates(), clearAll() and clear() could try to delete whole drive at wrong path permissions because realpath() fail (forum 25397) + - bugfix 'self::' and 'parent::' was interpreted in template syntax as static class + + 04.01.2015 + - push last weeks changes to github + + - different optimizations + - improvement automatically create different versions of compiled templates and config files depending + on property settings. + - optimization restructure template processing by moving code into classes it better belongs to + - optimization restructure config file processing + + 31.12.2014 + - bugfix use function_exists('mb_get_info') for setting Smarty::$_MBSTRING. + Function mb_split could be overloaded depending on php.ini mbstring.func_overload + + + 29.12.2014 + - new feature security can now limit the template nesting level by property $max_template_nesting + see also NEW_FEATURES.txt (forum 25370) + + 29.12.2014 + - new feature security can now disable special $smarty variables listed in property $disabled_special_smarty_vars + see also NEW_FEATURES.txt (forum 25370) + + 27.12.2014 + - bugfix clear internal _is_file_cache when plugins_dir was modified + + 13.12.2014 + - improvement optimization of lexer and parser resulting in a up to 30% higher compiling speed + + 11.12.2014 + - bugfix resolve parser ambiguity between constant print tag {CONST} and other smarty tags after change of 09.12.2014 + + 09.12.2014 + - bugfix variables $null, $true and $false did not work after the change of 12.11.2014 (forum 25342) + - bugfix call of template function by a variable name did not work after latest changes (forum 25342) + + 23.11.2014 + - bugfix a plugin with attached modifier could fail if the tag was immediately followed by another Smarty tag (since 3.1.21) (forum 25326) + + 13.11.2014 + - improvement move autoload code into Autoloader.php. Use Composer autoloader when possible + + 12.11.2014 + - new feature added support of namespaces to template code + + 08.11.2014 - 10.11.2014 + - bugfix subtemplate called in nocache mode could be called with wrong compile_id when it did change on one of the calling templates + - improvement add code of template functions called in nocache mode dynamically to cache file (related to bugfix of 01.11.2014) + - bugfix Debug Console did not include all data from merged compiled subtemplates + + 04.11.2014 + - new feature $smarty->debugging = true; => overwrite existing Debug Console window (old behaviour) + $smarty->debugging = 2; => individual Debug Console window by template name + + 03.11.2014 + - bugfix Debug Console did not show included subtemplates since 3.1.17 (forum 25301) + - bugfix Modifier debug_print_var did not limit recursion or prevent recursive object display at Debug Console + (ATTENTION: parameter order has changed to be able to specify maximum recursion) + - bugfix Debug consol did not include subtemplate information with $smarty->merge_compiled_includes = true + - improvement The template variables are no longer displayed as objects on the Debug Console + - improvement $smarty->createData($parent = null, $name = null) new optional name parameter for display at Debug Console + - addition of some hooks for future extension of Debug Console + + 01.11.2014 + - bugfix and enhancement on subtemplate {include} and template {function} tags. + * Calling a template which has a nocache section could fail if it was called from a cached and a not cached subtemplate. + * Calling the same subtemplate cached and not cached with the $smarty->merge_compiled_includes enabled could cause problems + * Many smaller related changes + + 30.10.2014 + - bugfix access to class constant by object like {$object::CONST} or variable class name {$class::CONST} did not work (forum 25301) + + 26.10.2014 + - bugfix E_NOTICE message was created during compilation when ASP tags '<%' or '%>' are in template source text + - bugfix merge_compiled_includes option failed when caching enables and same subtemplate was included cached and not cached + + ===== 3.1.21 ===== (18.10.2014) + 18.10.2014 + - composer moved to github + + 17.10.2014 + - bugfix on $php_handling security and optimization of smarty_internal_parsetree (Thue Kristensen) + + 16.10.2014 + - bugfix composer.json update + + 15.10.2014 + - bugfix calling a new created cache file with fetch() and Smarty::CACHING_LIFETIME_SAVED multiple times did fail (forum 22350) + + 14.10.2014 + - bugfix any tag placed within " diff --git a/vendor/smarty/smarty/libs/plugins/block.textformat.php b/vendor/smarty/smarty/libs/plugins/block.textformat.php new file mode 100644 index 00000000000..abf544939f2 --- /dev/null +++ b/vendor/smarty/smarty/libs/plugins/block.textformat.php @@ -0,0 +1,110 @@ + + * Name: textformat
+ * Purpose: format text a certain way with preset styles + * or custom wrap/indent settings
+ * Params: + *
+ * - style         - string (email)
+ * - indent        - integer (0)
+ * - wrap          - integer (80)
+ * - wrap_char     - string ("\n")
+ * - indent_char   - string (" ")
+ * - wrap_boundary - boolean (true)
+ * 
+ * + * @link http://www.smarty.net/manual/en/language.function.textformat.php {textformat} + * (Smarty online manual) + * + * @param array $params parameters + * @param string $content contents of the block + * @param Smarty_Internal_Template $template template object + * @param boolean &$repeat repeat flag + * + * @return string content re-formatted + * @author Monte Ohrt + */ +function smarty_block_textformat($params, $content, $template, &$repeat) +{ + if (is_null($content)) { + return; + } + + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string) $_val; + break; + + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int) $_val; + break; + + case 'wrap_cut': + $$_key = (bool) $_val; + break; + + default: + trigger_error("textformat: unknown attribute '$_key'"); + } + } + + if ($style == 'email') { + $wrap = 72; + } + // split into paragraphs + $_paragraphs = preg_split('![\r\n]{2}!', $content); + + foreach ($_paragraphs as &$_paragraph) { + if (!$_paragraph) { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraph = preg_replace(array('!\s+!' . Smarty::$_UTF8_MODIFIER, '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER), array(' ', ''), $_paragraph); + // indent first line + if ($indent_first > 0) { + $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; + } + // wordwrap sentences + if (Smarty::$_MBSTRING) { + require_once(SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'); + $_paragraph = smarty_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } else { + $_paragraph = wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } + // indent lines + if ($indent > 0) { + $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + + if ($assign) { + $template->assign($assign, $_output); + } else { + return $_output; + } +} diff --git a/vendor/smarty/smarty/libs/plugins/function.counter.php b/vendor/smarty/smarty/libs/plugins/function.counter.php new file mode 100644 index 00000000000..4da85a14e55 --- /dev/null +++ b/vendor/smarty/smarty/libs/plugins/function.counter.php @@ -0,0 +1,78 @@ + + * Name: counter
+ * Purpose: print out a counter value + * + * @author Monte Ohrt + * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} + * (Smarty online manual) + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ +function smarty_function_counter($params, $template) +{ + static $counters = array(); + + $name = (isset($params['name'])) ? $params['name'] : 'default'; + if (!isset($counters[$name])) { + $counters[$name] = array( + 'start' => 1, + 'skip' => 1, + 'direction' => 'up', + 'count' => 1 + ); + } + $counter =& $counters[$name]; + + if (isset($params['start'])) { + $counter['start'] = $counter['count'] = (int) $params['start']; + } + + if (!empty($params['assign'])) { + $counter['assign'] = $params['assign']; + } + + if (isset($counter['assign'])) { + $template->assign($counter['assign'], $counter['count']); + } + + if (isset($params['print'])) { + $print = (bool) $params['print']; + } else { + $print = empty($counter['assign']); + } + + if ($print) { + $retval = $counter['count']; + } else { + $retval = null; + } + + if (isset($params['skip'])) { + $counter['skip'] = $params['skip']; + } + + if (isset($params['direction'])) { + $counter['direction'] = $params['direction']; + } + + if ($counter['direction'] == "down") { + $counter['count'] -= $counter['skip']; + } else { + $counter['count'] += $counter['skip']; + } + + return $retval; +} diff --git a/vendor/smarty/smarty/libs/plugins/function.cycle.php b/vendor/smarty/smarty/libs/plugins/function.cycle.php new file mode 100644 index 00000000000..8dc5cd9d510 --- /dev/null +++ b/vendor/smarty/smarty/libs/plugins/function.cycle.php @@ -0,0 +1,107 @@ + + * Name: cycle
+ * Date: May 3, 2002
+ * Purpose: cycle through given values
+ * Params: + *
+ * - name      - name of cycle (optional)
+ * - values    - comma separated list of values to cycle, or an array of values to cycle
+ *               (this can be left out for subsequent calls)
+ * - reset     - boolean - resets given var to true
+ * - print     - boolean - print var or not. default is true
+ * - advance   - boolean - whether or not to advance the cycle
+ * - delimiter - the value delimiter, default is ","
+ * - assign    - boolean, assigns to template var instead of printed.
+ * 
+ * Examples:
+ *
+ * {cycle values="#eeeeee,#d0d0d0d"}
+ * {cycle name=row values="one,two,three" reset=true}
+ * {cycle name=row}
+ * 
+ * + * @link http://www.smarty.net/manual/en/language.function.cycle.php {cycle} + * (Smarty online manual) + * @author Monte Ohrt + * @author credit to Mark Priatel + * @author credit to Gerard + * @author credit to Jason Sweat + * @version 1.3 + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ + +function smarty_function_cycle($params, $template) +{ + static $cycle_vars; + + $name = (empty($params['name'])) ? 'default' : $params['name']; + $print = (isset($params['print'])) ? (bool) $params['print'] : true; + $advance = (isset($params['advance'])) ? (bool) $params['advance'] : true; + $reset = (isset($params['reset'])) ? (bool) $params['reset'] : false; + + if (!isset($params['values'])) { + if (!isset($cycle_vars[$name]['values'])) { + trigger_error("cycle: missing 'values' parameter"); + + return; + } + } else { + if (isset($cycle_vars[$name]['values']) + && $cycle_vars[$name]['values'] != $params['values'] + ) { + $cycle_vars[$name]['index'] = 0; + } + $cycle_vars[$name]['values'] = $params['values']; + } + + if (isset($params['delimiter'])) { + $cycle_vars[$name]['delimiter'] = $params['delimiter']; + } elseif (!isset($cycle_vars[$name]['delimiter'])) { + $cycle_vars[$name]['delimiter'] = ','; + } + + if (is_array($cycle_vars[$name]['values'])) { + $cycle_array = $cycle_vars[$name]['values']; + } else { + $cycle_array = explode($cycle_vars[$name]['delimiter'], $cycle_vars[$name]['values']); + } + + if (!isset($cycle_vars[$name]['index']) || $reset) { + $cycle_vars[$name]['index'] = 0; + } + + if (isset($params['assign'])) { + $print = false; + $template->assign($params['assign'], $cycle_array[$cycle_vars[$name]['index']]); + } + + if ($print) { + $retval = $cycle_array[$cycle_vars[$name]['index']]; + } else { + $retval = null; + } + + if ($advance) { + if ($cycle_vars[$name]['index'] >= count($cycle_array) - 1) { + $cycle_vars[$name]['index'] = 0; + } else { + $cycle_vars[$name]['index'] ++; + } + } + + return $retval; +} diff --git a/vendor/smarty/smarty/libs/plugins/function.fetch.php b/vendor/smarty/smarty/libs/plugins/function.fetch.php new file mode 100644 index 00000000000..3506d4a8d6e --- /dev/null +++ b/vendor/smarty/smarty/libs/plugins/function.fetch.php @@ -0,0 +1,221 @@ + + * Name: fetch
+ * Purpose: fetch file, web or ftp data and display results + * + * @link http://www.smarty.net/manual/en/language.function.fetch.php {fetch} + * (Smarty online manual) + * @author Monte Ohrt + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @throws SmartyException + * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable + */ +function smarty_function_fetch($params, $template) +{ + if (empty($params['file'])) { + trigger_error("[plugin] fetch parameter 'file' cannot be empty", E_USER_NOTICE); + + return; + } + + // strip file protocol + if (stripos($params['file'], 'file://') === 0) { + $params['file'] = substr($params['file'], 7); + } + + $protocol = strpos($params['file'], '://'); + if ($protocol !== false) { + $protocol = strtolower(substr($params['file'], 0, $protocol)); + } + + if (isset($template->smarty->security_policy)) { + if ($protocol) { + // remote resource (or php stream, …) + if (!$template->smarty->security_policy->isTrustedUri($params['file'])) { + return; + } + } else { + // local file + if (!$template->smarty->security_policy->isTrustedResourceDir($params['file'])) { + return; + } + } + } + + $content = ''; + if ($protocol == 'http') { + // http fetch + if ($uri_parts = parse_url($params['file'])) { + // set defaults + $host = $server_name = $uri_parts['host']; + $timeout = 30; + $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + $agent = "Smarty Template Engine " . Smarty::SMARTY_VERSION; + $referer = ""; + $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/'; + $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : ''; + $_is_proxy = false; + if (empty($uri_parts['port'])) { + $port = 80; + } else { + $port = $uri_parts['port']; + } + if (!empty($uri_parts['user'])) { + $user = $uri_parts['user']; + } + if (!empty($uri_parts['pass'])) { + $pass = $uri_parts['pass']; + } + // loop through parameters, setup headers + foreach ($params as $param_key => $param_value) { + switch ($param_key) { + case "file": + case "assign": + case "assign_headers": + break; + case "user": + if (!empty($param_value)) { + $user = $param_value; + } + break; + case "pass": + if (!empty($param_value)) { + $pass = $param_value; + } + break; + case "accept": + if (!empty($param_value)) { + $accept = $param_value; + } + break; + case "header": + if (!empty($param_value)) { + if (!preg_match('![\w\d-]+: .+!', $param_value)) { + trigger_error("[plugin] invalid header format '" . $param_value . "'", E_USER_NOTICE); + + return; + } else { + $extra_headers[] = $param_value; + } + } + break; + case "proxy_host": + if (!empty($param_value)) { + $proxy_host = $param_value; + } + break; + case "proxy_port": + if (!preg_match('!\D!', $param_value)) { + $proxy_port = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + break; + case "agent": + if (!empty($param_value)) { + $agent = $param_value; + } + break; + case "referer": + if (!empty($param_value)) { + $referer = $param_value; + } + break; + case "timeout": + if (!preg_match('!\D!', $param_value)) { + $timeout = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + break; + default: + trigger_error("[plugin] unrecognized attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + } + if (!empty($proxy_host) && !empty($proxy_port)) { + $_is_proxy = true; + $fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout); + } else { + $fp = fsockopen($server_name, $port, $errno, $errstr, $timeout); + } + + if (!$fp) { + trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE); + + return; + } else { + if ($_is_proxy) { + fputs($fp, 'GET ' . $params['file'] . " HTTP/1.0\r\n"); + } else { + fputs($fp, "GET $uri HTTP/1.0\r\n"); + } + if (!empty($host)) { + fputs($fp, "Host: $host\r\n"); + } + if (!empty($accept)) { + fputs($fp, "Accept: $accept\r\n"); + } + if (!empty($agent)) { + fputs($fp, "User-Agent: $agent\r\n"); + } + if (!empty($referer)) { + fputs($fp, "Referer: $referer\r\n"); + } + if (isset($extra_headers) && is_array($extra_headers)) { + foreach ($extra_headers as $curr_header) { + fputs($fp, $curr_header . "\r\n"); + } + } + if (!empty($user) && !empty($pass)) { + fputs($fp, "Authorization: BASIC " . base64_encode("$user:$pass") . "\r\n"); + } + + fputs($fp, "\r\n"); + while (!feof($fp)) { + $content .= fgets($fp, 4096); + } + fclose($fp); + $csplit = preg_split("!\r\n\r\n!", $content, 2); + + $content = $csplit[1]; + + if (!empty($params['assign_headers'])) { + $template->assign($params['assign_headers'], preg_split("!\r\n!", $csplit[0])); + } + } + } else { + trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE); + + return; + } + } else { + $content = @file_get_contents($params['file']); + if ($content === false) { + throw new SmartyException("{fetch} cannot read resource '" . $params['file'] . "'"); + } + } + + if (!empty($params['assign'])) { + $template->assign($params['assign'], $content); + } else { + return $content; + } +} diff --git a/vendor/smarty/smarty/libs/plugins/function.html_checkboxes.php b/vendor/smarty/smarty/libs/plugins/function.html_checkboxes.php new file mode 100644 index 00000000000..d7868036864 --- /dev/null +++ b/vendor/smarty/smarty/libs/plugins/function.html_checkboxes.php @@ -0,0 +1,237 @@ + + * Type: function
+ * Name: html_checkboxes
+ * Date: 24.Feb.2003
+ * Purpose: Prints out a list of checkbox input types
+ * Examples: + *
+ * {html_checkboxes values=$ids output=$names}
+ * {html_checkboxes values=$ids name='box' separator='
' output=$names} + * {html_checkboxes values=$ids checked=$checked separator='
' output=$names} + *
+ * Params: + *
+ * - name       (optional) - string default "checkbox"
+ * - values     (required) - array
+ * - options    (optional) - associative array
+ * - checked    (optional) - array default not set
+ * - separator  (optional) - ie 
or   + * - output (optional) - the output next to each checkbox + * - assign (optional) - assign the output as an array to this variable + * - escape (optional) - escape the content (not value), defaults to true + *
+ * + * @link http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} + * (Smarty online manual) + * @author Christopher Kvarme + * @author credits to Monte Ohrt + * @version 1.0 + * + * @param array $params parameters + * @param object $template template object + * + * @return string + * @uses smarty_function_escape_special_chars() + */ +function smarty_function_html_checkboxes($params, $template) +{ + require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'); + + $name = 'checkbox'; + $values = null; + $options = null; + $selected = array(); + $separator = ''; + $escape = true; + $labels = true; + $label_ids = false; + $output = null; + + $extra = ''; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'name': + case 'separator': + $$_key = (string) $_val; + break; + + case 'escape': + case 'labels': + case 'label_ids': + $$_key = (bool) $_val; + break; + + case 'options': + $$_key = (array) $_val; + break; + + case 'values': + case 'output': + $$_key = array_values((array) $_val); + break; + + case 'checked': + case 'selected': + if (is_array($_val)) { + $selected = array(); + foreach ($_val as $_sel) { + if (is_object($_sel)) { + if (method_exists($_sel, "__toString")) { + $_sel = smarty_function_escape_special_chars((string) $_sel->__toString()); + } else { + trigger_error("html_checkboxes: selected attribute contains an object of class '" . get_class($_sel) . "' without __toString() method", E_USER_NOTICE); + continue; + } + } else { + $_sel = smarty_function_escape_special_chars((string) $_sel); + } + $selected[$_sel] = true; + } + } elseif (is_object($_val)) { + if (method_exists($_val, "__toString")) { + $selected = smarty_function_escape_special_chars((string) $_val->__toString()); + } else { + trigger_error("html_checkboxes: selected attribute is an object of class '" . get_class($_val) . "' without __toString() method", E_USER_NOTICE); + } + } else { + $selected = smarty_function_escape_special_chars((string) $_val); + } + break; + + case 'checkboxes': + trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', E_USER_WARNING); + $options = (array) $_val; + break; + + case 'assign': + break; + + case 'strict': + break; + + case 'disabled': + case 'readonly': + if (!empty($params['strict'])) { + if (!is_scalar($_val)) { + trigger_error("html_options: $_key attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute", E_USER_NOTICE); + } + + if ($_val === true || $_val === $_key) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"'; + } + + break; + } + // omit break; to fall through! + + default: + if (!is_array($_val)) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"'; + } else { + trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + } + } + + if (!isset($options) && !isset($values)) { + return ''; + } /* raise error here? */ + + $_html_result = array(); + + if (isset($options)) { + foreach ($options as $_key => $_val) { + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); + } + } else { + foreach ($values as $_i => $_key) { + $_val = isset($output[$_i]) ? $output[$_i] : ''; + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); + } + } + + if (!empty($params['assign'])) { + $template->assign($params['assign'], $_html_result); + } else { + return implode("\n", $_html_result); + } +} + +function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids, $escape = true) +{ + $_output = ''; + + if (is_object($value)) { + if (method_exists($value, "__toString")) { + $value = (string) $value->__toString(); + } else { + trigger_error("html_options: value is an object of class '" . get_class($value) . "' without __toString() method", E_USER_NOTICE); + + return ''; + } + } else { + $value = (string) $value; + } + + if (is_object($output)) { + if (method_exists($output, "__toString")) { + $output = (string) $output->__toString(); + } else { + trigger_error("html_options: output is an object of class '" . get_class($output) . "' without __toString() method", E_USER_NOTICE); + + return ''; + } + } else { + $output = (string) $output; + } + + if ($labels) { + if ($label_ids) { + $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER, '_', $name . '_' . $value)); + $_output .= '

\n"; + } + + return $status; + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_internal_undefined.php b/vendor/smarty/smarty/libs/sysplugins/smarty_internal_undefined.php new file mode 100644 index 00000000000..93fca8ec1a8 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_internal_undefined.php @@ -0,0 +1,48 @@ +cached->valid = false; + } else { + $tpl->mustCompile = true; + } + return false; + } + + /** + * Call error handler for undefined method + * + * @param string $name unknown method-name + * @param array $args argument array + * + * @return mixed + * @throws SmartyException + */ + public function __call($name, $args) + { + throw new SmartyException(get_class($args[0]) . "->{$name}() undefined method"); + } +} \ No newline at end of file diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_resource.php b/vendor/smarty/smarty/libs/sysplugins/smarty_resource.php new file mode 100644 index 00000000000..93d9fa7a376 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_resource.php @@ -0,0 +1,270 @@ + 'smarty_internal_resource_file.php', + 'string' => 'smarty_internal_resource_string.php', + 'extends' => 'smarty_internal_resource_extends.php', + 'stream' => 'smarty_internal_resource_stream.php', + 'eval' => 'smarty_internal_resource_eval.php', + 'php' => 'smarty_internal_resource_php.php'); + + /** + * Flag if resource does implement populateCompiledFilepath() method + * + * @var bool + */ + public $hasCompiledHandler = false; + + /** + * Name of the Class to compile this resource's contents with + * + * @var string + */ + public $compiler_class = 'Smarty_Internal_SmartyTemplateCompiler'; + + /** + * Name of the Class to tokenize this resource's contents with + * + * @var string + */ + public $template_lexer_class = 'Smarty_Internal_Templatelexer'; + + /** + * Name of the Class to parse this resource's contents with + * + * @var string + */ + public $template_parser_class = 'Smarty_Internal_Templateparser'; + + /** + * Load template's source into current template object + * + * @param Smarty_Template_Source $source source object + * + * @return string template source + * @throws SmartyException if source cannot be loaded + */ + abstract public function getContent(Smarty_Template_Source $source); + + /** + * populate Source Object with meta data from Resource + * + * @param Smarty_Template_Source $source source object + * @param Smarty_Internal_Template $_template template object + */ + abstract public function populate(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null); + + /** + * populate Source Object with timestamp and exists from Resource + * + * @param Smarty_Template_Source $source source object + */ + public function populateTimestamp(Smarty_Template_Source $source) + { + // intentionally left blank + } + + /** + * modify resource_name according to resource handlers specifications + * + * @param Smarty $smarty Smarty instance + * @param string $resource_name resource_name to make unique + * @param boolean $isConfig flag for config resource + * + * @return string unique resource name + */ + public function buildUniqueResourceName(Smarty $smarty, $resource_name, $isConfig = false) + { + if ($isConfig) { + if (!isset($smarty->_joined_config_dir)) { + $smarty->getTemplateDir(null, true); + } + return get_class($this) . '#' . $smarty->_joined_config_dir . '#' . $resource_name; + } else { + if (!isset($smarty->_joined_template_dir)) { + $smarty->getTemplateDir(); + } + return get_class($this) . '#' . $smarty->_joined_template_dir . '#' . $resource_name; + } + } + + /** + * Determine basename for compiled filename + * + * @param Smarty_Template_Source $source source object + * + * @return string resource's basename + */ + public function getBasename(Smarty_Template_Source $source) + { + return null; + } + + /** + * Load Resource Handler + * + * @param Smarty $smarty smarty object + * @param string $type name of the resource + * + * @throws SmartyException + * @return Smarty_Resource Resource Handler + */ + public static function load(Smarty $smarty, $type) + { + // try smarty's cache + if (isset($smarty->_cache['resource_handlers'][$type])) { + return $smarty->_cache['resource_handlers'][$type]; + } + + // try registered resource + if (isset($smarty->registered_resources[$type])) { + return $smarty->_cache['resource_handlers'][$type] = + $smarty->registered_resources[$type] instanceof Smarty_Resource ? $smarty->registered_resources[$type] : + new Smarty_Internal_Resource_Registered(); + } + + // try sysplugins dir + if (isset(self::$sysplugins[$type])) { + $_resource_class = 'Smarty_Internal_Resource_' . ucfirst($type); + return $smarty->_cache['resource_handlers'][$type] = new $_resource_class(); + } + + // try plugins dir + $_resource_class = 'Smarty_Resource_' . ucfirst($type); + if ($smarty->loadPlugin($_resource_class)) { + if (class_exists($_resource_class, false)) { + return $smarty->_cache['resource_handlers'][$type] = new $_resource_class(); + } else { + $smarty->registerResource($type, + array("smarty_resource_{$type}_source", "smarty_resource_{$type}_timestamp", + "smarty_resource_{$type}_secure", "smarty_resource_{$type}_trusted")); + // give it another try, now that the resource is registered properly + return self::load($smarty, $type); + } + } + + // try streams + $_known_stream = stream_get_wrappers(); + if (in_array($type, $_known_stream)) { + // is known stream + if (is_object($smarty->security_policy)) { + $smarty->security_policy->isTrustedStream($type); + } + return $smarty->_cache['resource_handlers'][$type] = new Smarty_Internal_Resource_Stream(); + } + + // TODO: try default_(template|config)_handler + + // give up + throw new SmartyException("Unknown resource type '{$type}'"); + } + + /** + * extract resource_type and resource_name from template_resource and config_resource + * @note "C:/foo.tpl" was forced to file resource up till Smarty 3.1.3 (including). + * + * @param string $resource_name template_resource or config_resource to parse + * @param string $default_resource the default resource_type defined in $smarty + * + * @return array with parsed resource name and type + */ + public static function parseResourceName($resource_name, $default_resource) + { + if (preg_match('/^([A-Za-z0-9_\-]{2,})[:]/', $resource_name, $match)) { + $type = $match[1]; + $name = substr($resource_name, strlen($match[0])); + } else { + // no resource given, use default + // or single character before the colon is not a resource type, but part of the filepath + $type = $default_resource; + $name = $resource_name; + } + return array($name, $type); + } + + /** + * modify template_resource according to resource handlers specifications + * + * @param \Smarty_Internal_Template|\Smarty $obj Smarty instance + * @param string $template_resource template_resource to extract resource handler and name of + * + * @return string unique resource name + */ + public static function getUniqueTemplateName($obj, $template_resource) + { + $smarty = $obj->_objType == 2 ? $obj->smarty : $obj; + list($name, $type) = self::parseResourceName($template_resource, $smarty->default_resource_type); + // TODO: optimize for Smarty's internal resource types + $resource = Smarty_Resource::load($smarty, $type); + // go relative to a given template? + $_file_is_dotted = $name[0] == '.' && ($name[1] == '.' || $name[1] == '/'); + if ($obj->_objType == 2 && $_file_is_dotted && + ($obj->source->type == 'file' || $obj->parent->source->type == 'extends') + ) { + $name = dirname($obj->source->filepath) . DS . $name; + } + return $resource->buildUniqueResourceName($smarty, $name); + } + + /* + * Check if resource must check time stamps when when loading complied or cached templates. + * Resources like 'extends' which use source components my disable timestamp checks on own resource. + * + * @return bool + */ + public function checkTimestamps() { + return true; + } + + /** + * initialize Source Object for given resource + * wrapper for backward compatibility to versions < 3.1.22 + * Either [$_template] or [$smarty, $template_resource] must be specified + * + * @param Smarty_Internal_Template $_template template object + * @param Smarty $smarty smarty object + * @param string $template_resource resource identifier + * + * @return Smarty_Template_Source Source Object + */ + public static function source(Smarty_Internal_Template $_template = null, Smarty $smarty = null, + $template_resource = null) + { + return Smarty_Template_Source::load($_template, $smarty, $template_resource); + } +} + diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_resource_custom.php b/vendor/smarty/smarty/libs/sysplugins/smarty_resource_custom.php new file mode 100644 index 00000000000..619f2d6f01b --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_resource_custom.php @@ -0,0 +1,95 @@ +filepath = $source->type . ':' . $source->name; + $source->uid = sha1($source->type . ':' . $source->name); + + $mtime = $this->fetchTimestamp($source->name); + if ($mtime !== null) { + $source->timestamp = $mtime; + } else { + $this->fetch($source->name, $content, $timestamp); + $source->timestamp = isset($timestamp) ? $timestamp : false; + if (isset($content)) { + $source->content = $content; + } + } + $source->exists = !!$source->timestamp; + } + + /** + * Load template's source into current template object + * + * @param Smarty_Template_Source $source source object + * + * @return string template source + * @throws SmartyException if source cannot be loaded + */ + public function getContent(Smarty_Template_Source $source) + { + $this->fetch($source->name, $content, $timestamp); + if (isset($content)) { + return $content; + } + + throw new SmartyException("Unable to read template {$source->type} '{$source->name}'"); + } + + /** + * Determine basename for compiled filename + * + * @param Smarty_Template_Source $source source object + * + * @return string resource's basename + */ + public function getBasename(Smarty_Template_Source $source) + { + return basename($source->name); + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_resource_recompiled.php b/vendor/smarty/smarty/libs/sysplugins/smarty_resource_recompiled.php new file mode 100644 index 00000000000..cfd73401ba7 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_resource_recompiled.php @@ -0,0 +1,47 @@ +filepath = false; + $compiled->timestamp = false; + $compiled->exists = false; + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_resource_uncompiled.php b/vendor/smarty/smarty/libs/sysplugins/smarty_resource_uncompiled.php new file mode 100644 index 00000000000..88d2bba81bd --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_resource_uncompiled.php @@ -0,0 +1,79 @@ +filepath = false; + $compiled->timestamp = false; + $compiled->exists = false; + } + + /** + * render compiled template code + * + * @param Smarty_Internal_Template $_template + * + * @return string + * @throws Exception + */ + public function render($_template) + { + $level = ob_get_level(); + ob_start(); + try { + $this->renderUncompiled($_template->source, $_template); + return ob_get_clean(); + } + catch (Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + throw $e; + } + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_security.php b/vendor/smarty/smarty/libs/sysplugins/smarty_security.php new file mode 100644 index 00000000000..9d48bcb2188 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_security.php @@ -0,0 +1,715 @@ +" tags in templates. + * possible values: + *
    + *
  • Smarty::PHP_PASSTHRU -> echo PHP tags as they are
  • + *
  • Smarty::PHP_QUOTE -> escape tags as entities
  • + *
  • Smarty::PHP_REMOVE -> remove php tags
  • + *
  • Smarty::PHP_ALLOW -> execute php tags
  • + *
+ * + * @var integer + */ + public $php_handling = Smarty::PHP_PASSTHRU; + + /** + * This is the list of template directories that are considered secure. + * $template_dir is in this list implicitly. + * + * @var array + */ + public $secure_dir = array(); + + /** + * This is an array of directories where trusted php scripts reside. + * {@link $security} is disabled during their inclusion/execution. + * + * @var array + */ + public $trusted_dir = array(); + + /** + * List of regular expressions (PCRE) that include trusted URIs + * + * @var array + */ + public $trusted_uri = array(); + + /** + * List of trusted constants names + * + * @var array + */ + public $trusted_constants = array(); + + /** + * This is an array of trusted static classes. + * If empty access to all static classes is allowed. + * If set to 'none' none is allowed. + * + * @var array + */ + public $static_classes = array(); + + /** + * This is an nested array of trusted classes and static methods. + * If empty access to all static classes and methods is allowed. + * Format: + * array ( + * 'class_1' => array('method_1', 'method_2'), // allowed methods listed + * 'class_2' => array(), // all methods of class allowed + * ) + * If set to null none is allowed. + * + * @var array + */ + public $trusted_static_methods = array(); + + /** + * This is an array of trusted static properties. + * If empty access to all static classes and properties is allowed. + * Format: + * array ( + * 'class_1' => array('prop_1', 'prop_2'), // allowed properties listed + * 'class_2' => array(), // all properties of class allowed + * ) + * If set to null none is allowed. + * + * @var array + */ + public $trusted_static_properties = array(); + + /** + * This is an array of trusted PHP functions. + * If empty all functions are allowed. + * To disable all PHP functions set $php_functions = null. + * + * @var array + */ + public $php_functions = array('isset', 'empty', 'count', 'sizeof', 'in_array', 'is_array', 'time',); + + /** + * This is an array of trusted PHP modifiers. + * If empty all modifiers are allowed. + * To disable all modifier set $php_modifiers = null. + * + * @var array + */ + public $php_modifiers = array('escape', 'count', 'nl2br',); + + /** + * This is an array of allowed tags. + * If empty no restriction by allowed_tags. + * + * @var array + */ + public $allowed_tags = array(); + + /** + * This is an array of disabled tags. + * If empty no restriction by disabled_tags. + * + * @var array + */ + public $disabled_tags = array(); + + /** + * This is an array of allowed modifier plugins. + * If empty no restriction by allowed_modifiers. + * + * @var array + */ + public $allowed_modifiers = array(); + + /** + * This is an array of disabled modifier plugins. + * If empty no restriction by disabled_modifiers. + * + * @var array + */ + public $disabled_modifiers = array(); + + /** + * This is an array of disabled special $smarty variables. + * + * @var array + */ + public $disabled_special_smarty_vars = array(); + + /** + * This is an array of trusted streams. + * If empty all streams are allowed. + * To disable all streams set $streams = null. + * + * @var array + */ + public $streams = array('file'); + + /** + * + flag if constants can be accessed from template + * + * @var boolean + */ + public $allow_constants = true; + + /** + * + flag if super globals can be accessed from template + * + * @var boolean + */ + public $allow_super_globals = true; + + /** + * max template nesting level + * + * @var int + */ + public $max_template_nesting = 0; + + /** + * current template nesting level + * + * @var int + */ + private $_current_template_nesting = 0; + + /** + * Cache for $resource_dir lookup + * + * @var array + */ + protected $_resource_dir = array(); + + /** + * Cache for $template_dir lookup + * + * @var array + */ + protected $_template_dir = array(); + + /** + * Cache for $config_dir lookup + * + * @var array + */ + protected $_config_dir = array(); + + /** + * Cache for $secure_dir lookup + * + * @var array + */ + protected $_secure_dir = array(); + + /** + * Cache for $php_resource_dir lookup + * + * @var array + */ + protected $_php_resource_dir = null; + + /** + * Cache for $trusted_dir lookup + * + * @var array + */ + protected $_trusted_dir = null; + + /** + * Cache for include path status + * + * @var bool + */ + protected $_include_path_status = false; + + /** + * Cache for $_include_array lookup + * + * @var array + */ + protected $_include_dir = array(); + + /** + * @param Smarty $smarty + */ + public function __construct($smarty) + { + $this->smarty = $smarty; + } + + /** + * Check if PHP function is trusted. + * + * @param string $function_name + * @param object $compiler compiler object + * + * @return boolean true if function is trusted + * @throws SmartyCompilerException if php function is not trusted + */ + public function isTrustedPhpFunction($function_name, $compiler) + { + if (isset($this->php_functions) && + (empty($this->php_functions) || in_array($function_name, $this->php_functions)) + ) { + return true; + } + + $compiler->trigger_template_error("PHP function '{$function_name}' not allowed by security setting"); + + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if static class is trusted. + * + * @param string $class_name + * @param object $compiler compiler object + * + * @return boolean true if class is trusted + * @throws SmartyCompilerException if static class is not trusted + */ + public function isTrustedStaticClass($class_name, $compiler) + { + if (isset($this->static_classes) && + (empty($this->static_classes) || in_array($class_name, $this->static_classes)) + ) { + return true; + } + + $compiler->trigger_template_error("access to static class '{$class_name}' not allowed by security setting"); + + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if static class method/property is trusted. + * + * @param string $class_name + * @param string $params + * @param object $compiler compiler object + * + * @return boolean true if class method is trusted + * @throws SmartyCompilerException if static class method is not trusted + */ + public function isTrustedStaticClassAccess($class_name, $params, $compiler) + { + if (!isset($params[2])) { + // fall back + return $this->isTrustedStaticClass($class_name, $compiler); + } + if ($params[2] == 'method') { + $allowed = $this->trusted_static_methods; + $name = substr($params[0], 0, strpos($params[0], '(')); + } else { + $allowed = $this->trusted_static_properties; + // strip '$' + $name = substr($params[0], 1); + } + if (isset($allowed)) { + if (empty($allowed)) { + // fall back + return $this->isTrustedStaticClass($class_name, $compiler); + } + if (isset($allowed[$class_name]) && + (empty($allowed[$class_name]) || in_array($name, $allowed[$class_name])) + ) { + return true; + } + } + $compiler->trigger_template_error("access to static class '{$class_name}' {$params[2]} '{$name}' not allowed by security setting"); + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if PHP modifier is trusted. + * + * @param string $modifier_name + * @param object $compiler compiler object + * + * @return boolean true if modifier is trusted + * @throws SmartyCompilerException if modifier is not trusted + */ + public function isTrustedPhpModifier($modifier_name, $compiler) + { + if (isset($this->php_modifiers) && + (empty($this->php_modifiers) || in_array($modifier_name, $this->php_modifiers)) + ) { + return true; + } + + $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting"); + + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if tag is trusted. + * + * @param string $tag_name + * @param object $compiler compiler object + * + * @return boolean true if tag is trusted + * @throws SmartyCompilerException if modifier is not trusted + */ + public function isTrustedTag($tag_name, $compiler) + { + // check for internal always required tags + if (in_array($tag_name, array('assign', 'call', 'private_filter', 'private_block_plugin', + 'private_function_plugin', 'private_object_block_function', + 'private_object_function', 'private_registered_function', + 'private_registered_block', 'private_special_variable', + 'private_print_expression', 'private_modifier'))) { + return true; + } + // check security settings + if (empty($this->allowed_tags)) { + if (empty($this->disabled_tags) || !in_array($tag_name, $this->disabled_tags)) { + return true; + } else { + $compiler->trigger_template_error("tag '{$tag_name}' disabled by security setting", null, true); + } + } elseif (in_array($tag_name, $this->allowed_tags) && !in_array($tag_name, $this->disabled_tags)) { + return true; + } else { + $compiler->trigger_template_error("tag '{$tag_name}' not allowed by security setting", null, true); + } + + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if special $smarty variable is trusted. + * + * @param string $var_name + * @param object $compiler compiler object + * + * @return boolean true if tag is trusted + * @throws SmartyCompilerException if modifier is not trusted + */ + public function isTrustedSpecialSmartyVar($var_name, $compiler) + { + if (!in_array($var_name, $this->disabled_special_smarty_vars)) { + return true; + } else { + $compiler->trigger_template_error("special variable '\$smarty.{$var_name}' not allowed by security setting", null, true); + } + + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if modifier plugin is trusted. + * + * @param string $modifier_name + * @param object $compiler compiler object + * + * @return boolean true if tag is trusted + * @throws SmartyCompilerException if modifier is not trusted + */ + public function isTrustedModifier($modifier_name, $compiler) + { + // check for internal always allowed modifier + if (in_array($modifier_name, array('default'))) { + return true; + } + // check security settings + if (empty($this->allowed_modifiers)) { + if (empty($this->disabled_modifiers) || !in_array($modifier_name, $this->disabled_modifiers)) { + return true; + } else { + $compiler->trigger_template_error("modifier '{$modifier_name}' disabled by security setting", null, true); + } + } elseif (in_array($modifier_name, $this->allowed_modifiers) && + !in_array($modifier_name, $this->disabled_modifiers) + ) { + return true; + } else { + $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting", null, true); + } + + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if constants are enabled or trusted + * + * @param string $const constant name + * @param object $compiler compiler object + * + * @return bool + */ + public function isTrustedConstant($const, $compiler) + { + if (in_array($const, array('true', 'false', 'null'))) { + return true; + } + if (!empty($this->trusted_constants)) { + if (!in_array($const, $this->trusted_constants)) { + $compiler->trigger_template_error("Security: access to constant '{$const}' not permitted"); + return false; + } + return true; + } + if ($this->allow_constants) { + return true; + } + $compiler->trigger_template_error("Security: access to constants not permitted"); + return false; + } + + /** + * Check if stream is trusted. + * + * @param string $stream_name + * + * @return boolean true if stream is trusted + * @throws SmartyException if stream is not trusted + */ + public function isTrustedStream($stream_name) + { + if (isset($this->streams) && (empty($this->streams) || in_array($stream_name, $this->streams))) { + return true; + } + + throw new SmartyException("stream '{$stream_name}' not allowed by security setting"); + } + + /** + * Check if directory of file resource is trusted. + * + * @param string $filepath + * @param null|bool $isConfig + * + * @return bool true if directory is trusted + * @throws \SmartyException if directory is not trusted + */ + public function isTrustedResourceDir($filepath, $isConfig = null) + { + if ($this->_include_path_status !== $this->smarty->use_include_path) { + foreach ($this->_include_dir as $directory) { + unset($this->_resource_dir[$directory]); + } + if ($this->smarty->use_include_path) { + $this->_include_dir = array(); + $_dirs = $this->smarty->ext->_getIncludePath->getIncludePathDirs($this->smarty); + foreach ($_dirs as $directory) { + $this->_include_dir[] = $directory; + $this->_resource_dir[$directory] = true; + } + } + $this->_include_path_status = $this->smarty->use_include_path; + } + if ($isConfig !== true && + (!isset($this->smarty->_cache['template_dir_new']) || $this->smarty->_cache['template_dir_new']) + ) { + $_dir = $this->smarty->getTemplateDir(); + if ($this->_template_dir !== $_dir) { + foreach ($this->_template_dir as $directory) { + unset($this->_resource_dir[$directory]); + } + foreach ($_dir as $directory) { + $this->_resource_dir[$directory] = true; + } + $this->_template_dir = $_dir; + } + $this->smarty->_cache['template_dir_new'] = false; + } + if ($isConfig !== false && + (!isset($this->smarty->_cache['config_dir_new']) || $this->smarty->_cache['config_dir_new']) + ) { + $_dir = $this->smarty->getConfigDir(); + if ($this->_config_dir !== $_dir) { + foreach ($this->_config_dir as $directory) { + unset($this->_resource_dir[$directory]); + } + foreach ($_dir as $directory) { + $this->_resource_dir[$directory] = true; + } + $this->_config_dir = $_dir; + } + $this->smarty->_cache['config_dir_new'] = false; + } + if ($this->_secure_dir !== (array) $this->secure_dir) { + foreach ($this->_secure_dir as $directory) { + unset($this->_resource_dir[$directory]); + } + foreach ((array) $this->secure_dir as $directory) { + $directory = $this->smarty->_realpath($directory . DS, true); + $this->_resource_dir[$directory] = true; + } + $this->_secure_dir = (array) $this->secure_dir; + } + $this->_resource_dir = $this->_checkDir($filepath, $this->_resource_dir); + return true; + } + + /** + * Check if URI (e.g. {fetch} or {html_image}) is trusted + * To simplify things, isTrustedUri() resolves all input to "{$PROTOCOL}://{$HOSTNAME}". + * So "http://username:password@hello.world.example.org:8080/some-path?some=query-string" + * is reduced to "http://hello.world.example.org" prior to applying the patters from {@link $trusted_uri}. + * + * @param string $uri + * + * @return boolean true if URI is trusted + * @throws SmartyException if URI is not trusted + * @uses $trusted_uri for list of patterns to match against $uri + */ + public function isTrustedUri($uri) + { + $_uri = parse_url($uri); + if (!empty($_uri['scheme']) && !empty($_uri['host'])) { + $_uri = $_uri['scheme'] . '://' . $_uri['host']; + foreach ($this->trusted_uri as $pattern) { + if (preg_match($pattern, $_uri)) { + return true; + } + } + } + + throw new SmartyException("URI '{$uri}' not allowed by security setting"); + } + + /** + * Check if directory of file resource is trusted. + * + * @param string $filepath + * + * @return boolean true if directory is trusted + * @throws SmartyException if PHP directory is not trusted + */ + public function isTrustedPHPDir($filepath) + { + if (empty($this->trusted_dir)) { + throw new SmartyException("directory '{$filepath}' not allowed by security setting (no trusted_dir specified)"); + } + + // check if index is outdated + if (!$this->_trusted_dir || $this->_trusted_dir !== $this->trusted_dir) { + $this->_php_resource_dir = array(); + + $this->_trusted_dir = $this->trusted_dir; + foreach ((array) $this->trusted_dir as $directory) { + $directory = $this->smarty->_realpath($directory . DS, true); + $this->_php_resource_dir[$directory] = true; + } + } + + $this->_php_resource_dir = $this->_checkDir($this->smarty->_realpath($filepath, true), $this->_php_resource_dir); + return true; + } + + /** + * Start template processing + * + * @param $template + * + * @throws SmartyException + */ + public function startTemplate($template) + { + if ($this->max_template_nesting > 0 && $this->_current_template_nesting ++ >= $this->max_template_nesting) { + throw new SmartyException("maximum template nesting level of '{$this->max_template_nesting}' exceeded when calling '{$template->template_resource}'"); + } + } + + /** + * Exit template processing + * + * @internal param $template + */ + public function exitTemplate() + { + if ($this->max_template_nesting > 0) { + $this->_current_template_nesting --; + } + } + + /** + * Check if file is inside a valid directory + * + * @param string $filepath + * @param array $dirs valid directories + * + * @return array + * @throws \SmartyException + */ + private function _checkDir($filepath, $dirs) + { + $directory = dirname($filepath) . DS; + $_directory = array(); + while (true) { + // remember the directory to add it to _resource_dir in case we're successful + $_directory[$directory] = true; + // test if the directory is trusted + if (isset($dirs[$directory])) { + // merge sub directories of current $directory into _resource_dir to speed up subsequent lookup + $dirs = array_merge($dirs, $_directory); + + return $dirs; + } + // abort if we've reached root + if (!preg_match('#[\\\/][^\\\/]+[\\\/]$#', $directory)) { + break; + } + // bubble up one level + $directory = preg_replace('#[\\\/][^\\\/]+[\\\/]$#', DS, $directory); + } + + // give up + throw new SmartyException("directory '{$filepath}' not allowed by security setting"); + } + + /** + * Loads security class and enables security + * + * @param \Smarty $smarty + * @param string|Smarty_Security $security_class if a string is used, it must be class-name + * + * @return \Smarty current Smarty instance for chaining + * @throws \SmartyException when an invalid class name is provided + */ + public static function enableSecurity(Smarty $smarty, $security_class) + { + if ($security_class instanceof Smarty_Security) { + $smarty->security_policy = $security_class; + return; + } elseif (is_object($security_class)) { + throw new SmartyException("Class '" . get_class($security_class) . "' must extend Smarty_Security."); + } + if ($security_class == null) { + $security_class = $smarty->security_class; + } + if (!class_exists($security_class)) { + throw new SmartyException("Security class '$security_class' is not defined"); + } elseif ($security_class !== 'Smarty_Security' && !is_subclass_of($security_class, 'Smarty_Security')) { + throw new SmartyException("Class '$security_class' must extend Smarty_Security."); + } else { + $smarty->security_policy = new $security_class($smarty); + } + return; + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_template_cached.php b/vendor/smarty/smarty/libs/sysplugins/smarty_template_cached.php new file mode 100644 index 00000000000..cecf4298608 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_template_cached.php @@ -0,0 +1,246 @@ +compile_id = $_template->compile_id; + $this->cache_id = $_template->cache_id; + $this->source = $_template->source; + if (!class_exists('Smarty_CacheResource', false)) { + require SMARTY_SYSPLUGINS_DIR . 'smarty_cacheresource.php'; + } + $this->handler = Smarty_CacheResource::load($_template->smarty); + } + + /** + * @param Smarty_Internal_Template $_template + * + * @return Smarty_Template_Cached + */ + static function load(Smarty_Internal_Template $_template) + { + $_template->cached = new Smarty_Template_Cached($_template); + $_template->cached->handler->populate($_template->cached, $_template); + // caching enabled ? + if (!($_template->caching == Smarty::CACHING_LIFETIME_CURRENT || + $_template->caching == Smarty::CACHING_LIFETIME_SAVED) || $_template->source->handler->recompiled + ) { + $_template->cached->valid = false; + } + return $_template->cached; + } + + /** + * Render cache template + * + * @param \Smarty_Internal_Template $_template + * @param bool $no_output_filter + * + * @throws \Exception + */ + public function render(Smarty_Internal_Template $_template, $no_output_filter = true) + { + if ($this->isCached($_template)) { + if ($_template->smarty->debugging) { + $_template->smarty->_debug->start_cache($_template); + } + if (!$this->processed) { + $this->process($_template); + } + $this->getRenderedTemplateCode($_template); + if ($_template->smarty->debugging) { + $_template->smarty->_debug->end_cache($_template); + } + return; + } else { + $_template->smarty->ext->_updateCache->updateCache($this, $_template, $no_output_filter); + } + } + + /** + * Check if cache is valid, lock cache if required + * + * @param \Smarty_Internal_Template $_template + * + * @return bool flag true if cache is valid + */ + public function isCached(Smarty_Internal_Template $_template) + { + if ($this->valid !== null) { + return $this->valid; + } + while (true) { + while (true) { + if ($this->exists === false || $_template->smarty->force_compile || $_template->smarty->force_cache) { + $this->valid = false; + } else { + $this->valid = true; + } + if ($this->valid && $_template->caching == Smarty::CACHING_LIFETIME_CURRENT && + $_template->cache_lifetime >= 0 && time() > ($this->timestamp + $_template->cache_lifetime) + ) { + // lifetime expired + $this->valid = false; + } + if ($this->valid && $_template->smarty->compile_check == 1 && + $_template->source->getTimeStamp() > $this->timestamp + ) { + $this->valid = false; + } + if ($this->valid || !$_template->smarty->cache_locking) { + break; + } + if (!$this->handler->locked($_template->smarty, $this)) { + $this->handler->acquireLock($_template->smarty, $this); + break 2; + } + $this->handler->populate($this, $_template); + } + if ($this->valid) { + if (!$_template->smarty->cache_locking || $this->handler->locked($_template->smarty, $this) === null) { + // load cache file for the following checks + if ($_template->smarty->debugging) { + $_template->smarty->_debug->start_cache($_template); + } + if ($this->handler->process($_template, $this) === false) { + $this->valid = false; + } else { + $this->processed = true; + } + if ($_template->smarty->debugging) { + $_template->smarty->_debug->end_cache($_template); + } + } else { + $this->is_locked = true; + continue; + } + } else { + return $this->valid; + } + if ($this->valid && $_template->caching === Smarty::CACHING_LIFETIME_SAVED && + $_template->cached->cache_lifetime >= 0 && + (time() > ($_template->cached->timestamp + $_template->cached->cache_lifetime)) + ) { + $this->valid = false; + } + if ($_template->smarty->cache_locking) { + if (!$this->valid) { + $this->handler->acquireLock($_template->smarty, $this); + } elseif ($this->is_locked) { + $this->handler->releaseLock($_template->smarty, $this); + } + } + return $this->valid; + } + return $this->valid; + } + + /** + * Process cached template + * + * @param Smarty_Internal_Template $_template template object + * @param bool $update flag if called because cache update + */ + public function process(Smarty_Internal_Template $_template, $update = false) + { + if ($this->handler->process($_template, $this, $update) === false) { + $this->valid = false; + } + if ($this->valid) { + $this->processed = true; + } else { + $this->processed = false; + } + } + + /** + * Read cache content from handler + * + * @param Smarty_Internal_Template $_template template object + * + * @return string content + */ + public function read(Smarty_Internal_Template $_template) + { + if (!$_template->source->handler->recompiled) { + return $this->handler->readCachedContent($_template); + } + return false; + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_template_compiled.php b/vendor/smarty/smarty/libs/sysplugins/smarty_template_compiled.php new file mode 100644 index 00000000000..e7710f85e6b --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_template_compiled.php @@ -0,0 +1,297 @@ +source->handler->recompiled && + ($_template->smarty->resource_cache_mode & Smarty::RESOURCE_CACHE_ON) + ) { + $_cache_key = $_template->source->unique_resource . '#'; + if ($_template->caching) { + $_cache_key .= 'caching#'; + } + $_cache_key .= $_template->compile_id; + if (isset($_template->source->compileds[$_cache_key])) { + return $_template->source->compileds[$_cache_key]; + } + } + $compiled = new Smarty_Template_Compiled(); + if ($_template->source->handler->hasCompiledHandler) { + $_template->source->handler->populateCompiledFilepath($compiled, $_template); + } else { + $compiled->populateCompiledFilepath($_template); + } + // runtime cache + if (!$_template->source->handler->recompiled && + ($_template->smarty->resource_cache_mode & Smarty::RESOURCE_CACHE_ON) + ) { + $_template->source->compileds[$_cache_key] = $compiled; + } + return $compiled; + } + + /** + * populate Compiled Object with compiled filepath + * + * @param Smarty_Internal_Template $_template template object + **/ + public function populateCompiledFilepath(Smarty_Internal_Template $_template) + { + $_compile_id = isset($_template->compile_id) ? preg_replace('![^\w]+!', '_', $_template->compile_id) : null; + if ($_template->source->isConfig) { + $_flag = '_' . + ((int) $_template->smarty->config_read_hidden + (int) $_template->smarty->config_booleanize * 2 + + (int) $_template->smarty->config_overwrite * 4); + } else { + $_flag = + '_' . ((int) $_template->smarty->merge_compiled_includes + (int) $_template->smarty->escape_html * 2); + } + $_filepath = $_template->source->uid . $_flag; + // if use_sub_dirs, break file into directories + if ($_template->smarty->use_sub_dirs) { + $_filepath = substr($_filepath, 0, 2) . DS . substr($_filepath, 2, 2) . DS . substr($_filepath, 4, 2) . DS . + $_filepath; + } + $_compile_dir_sep = $_template->smarty->use_sub_dirs ? DS : '^'; + if (isset($_compile_id)) { + $_filepath = $_compile_id . $_compile_dir_sep . $_filepath; + } + // caching token + if ($_template->caching) { + $_cache = '.cache'; + } else { + $_cache = ''; + } + $_compile_dir = $_template->smarty->getCompileDir(); + // set basename if not specified + $_basename = $_template->source->handler->getBasename($_template->source); + if ($_basename === null) { + $_basename = basename(preg_replace('![^\w]+!', '_', $_template->source->name)); + } + // separate (optional) basename by dot + if ($_basename) { + $_basename = '.' . $_basename; + } + + $this->filepath = $_compile_dir . $_filepath . '.' . $_template->source->type . $_basename . $_cache . '.php'; + $this->exists = is_file($this->filepath); + if (!$this->exists) { + $this->timestamp = false; + } + } + + /** + * load compiled template or compile from source + * + * @param Smarty_Internal_Template $_template + * + * @throws Exception + */ + public function process(Smarty_Internal_Template $_template) + { + $_smarty_tpl = $_template; + if ($_template->source->handler->recompiled || !$_template->compiled->exists || + $_template->smarty->force_compile || ($_template->smarty->compile_check && + $_template->source->getTimeStamp() > $_template->compiled->getTimeStamp()) + ) { + $this->compileTemplateSource($_template); + $compileCheck = $_template->smarty->compile_check; + $_template->smarty->compile_check = false; + if ($_template->source->handler->recompiled) { + $level = ob_get_level(); + ob_start(); + try { + eval("?>" . $this->content); + } + catch (Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + throw $e; + } + ob_get_clean(); + $this->content = null; + } else { + $this->loadCompiledTemplate($_template); + } + $_template->smarty->compile_check = $compileCheck; + } else { + $_template->mustCompile = true; + @include($_template->compiled->filepath); + if ($_template->mustCompile) { + $this->compileTemplateSource($_template); + $compileCheck = $_template->smarty->compile_check; + $_template->smarty->compile_check = false; + $this->loadCompiledTemplate($_template); + $_template->smarty->compile_check = $compileCheck; + } + } + $_template->smarty->ext->_subTemplate->registerSubTemplates($_template); + + $this->processed = true; + } + + /** + * Load fresh compiled template by including the PHP file + * HHVM requires a work around because of a PHP incompatibility + * + * @param \Smarty_Internal_Template $_template + */ + private function loadCompiledTemplate(Smarty_Internal_Template $_template) + { + if (function_exists('opcache_invalidate')) { + opcache_invalidate($_template->compiled->filepath); + } + $_smarty_tpl = $_template; + if (defined('HHVM_VERSION')) { + $_template->smarty->ext->_hhvm->includeHhvm($_template, $_template->compiled->filepath); + } else { + include($_template->compiled->filepath); + } + } + + /** + * render compiled template code + * + * @param Smarty_Internal_Template $_template + * + * @return string + * @throws Exception + */ + public function render(Smarty_Internal_Template $_template) + { + if ($_template->smarty->debugging) { + $_template->smarty->_debug->start_render($_template); + } + if (!$this->processed) { + $this->process($_template); + } + if (isset($_template->cached)) { + $_template->cached->file_dependency = + array_merge($_template->cached->file_dependency, $this->file_dependency); + } + $this->getRenderedTemplateCode($_template); + if ($_template->caching && $this->has_nocache_code) { + $_template->cached->hashes[$this->nocache_hash] = true; + } + if (isset($_template->parent) && $_template->parent->_objType == 2 && !empty($_template->tpl_function)) { + $_template->parent->tpl_function = array_merge($_template->parent->tpl_function, $_template->tpl_function); + } + if ($_template->smarty->debugging) { + $_template->smarty->_debug->end_render($_template); + } + } + + /** + * compile template from source + * + * @param Smarty_Internal_Template $_template + * + * @return string + * @throws Exception + */ + public function compileTemplateSource(Smarty_Internal_Template $_template) + { + $_template->source->compileds = array(); + $this->file_dependency = array(); + $this->tpl_function = array(); + $this->includes = array(); + $this->nocache_hash = null; + $this->unifunc = null; + // compile locking + if (!$_template->source->handler->recompiled) { + if ($saved_timestamp = $_template->compiled->getTimeStamp()) { + touch($_template->compiled->filepath); + } + } + // call compiler + try { + $_template->loadCompiler(); + $code = $_template->compiler->compileTemplate($_template); + } + catch (Exception $e) { + // restore old timestamp in case of error + if (!$_template->source->handler->recompiled && $saved_timestamp) { + touch($_template->compiled->filepath, $saved_timestamp); + } + throw $e; + } + // compiling succeeded + if ($_template->compiler->write_compiled_code) { + // write compiled template + $this->write($_template, $code); + $code = ''; + } + // release compiler object to free memory + unset($_template->compiler); + return $code; + } + + /** + * Write compiled code by handler + * + * @param Smarty_Internal_Template $_template template object + * @param string $code compiled code + * + * @return boolean success + */ + public function write(Smarty_Internal_Template $_template, $code) + { + if (!$_template->source->handler->recompiled) { + if ($_template->smarty->ext->_writeFile->writeFile($this->filepath, $code, $_template->smarty) === true) { + $this->timestamp = $this->exists = is_file($this->filepath); + if ($this->exists) { + $this->timestamp = filemtime($this->filepath); + return true; + } + } + return false; + } else { + $this->content = $code; + } + $this->timestamp = time(); + $this->exists = true; + return true; + } + + /** + * Read compiled content from handler + * + * @param Smarty_Internal_Template $_template template object + * + * @return string content + */ + public function read(Smarty_Internal_Template $_template) + { + if (!$_template->source->handler->recompiled) { + return file_get_contents($this->filepath); + } + return isset($this->content) ? $this->content : false; + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_template_config.php b/vendor/smarty/smarty/libs/sysplugins/smarty_template_config.php new file mode 100644 index 00000000000..f0fff50804e --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_template_config.php @@ -0,0 +1,97 @@ +handler = clone $handler; // Note: prone to circular references + $this->handler->compiler_class = 'Smarty_Internal_Config_File_Compiler'; + $this->handler->template_lexer_class = 'Smarty_Internal_Configfilelexer'; + $this->handler->template_parser_class = 'Smarty_Internal_Configfileparser'; + $this->resource = $resource; + $this->type = $type; + $this->name = $name; + $this->smarty = $smarty; + } + + /** + * initialize Source Object for given resource + * Either [$_template] or [$smarty, $template_resource] must be specified + * + * @param Smarty_Internal_Template $_template template object + * @param Smarty $smarty smarty object + * @param string $template_resource resource identifier + * + * @return Smarty_Template_Config Source Object + * @throws SmartyException + */ + public static function load(Smarty_Internal_Template $_template = null, Smarty $smarty = null, $template_resource = null) + { + static $_incompatible_resources = array('extends' => true, 'php' => true); + $template_resource = $_template->template_resource; + if (empty($template_resource)) { + throw new SmartyException('Missing config name'); + } + // parse resource_name, load resource handler + list($name, $type) = Smarty_Resource::parseResourceName($template_resource, $_template->smarty->default_config_type); + // make sure configs are not loaded via anything smarty can't handle + if (isset($_incompatible_resources[$type])) { + throw new SmartyException ("Unable to use resource '{$type}' for config"); + } + $resource = Smarty_Resource::load($_template->smarty, $type); + $source = new Smarty_Template_Config($resource, $_template->smarty, $template_resource, $type, $name); + $resource->populate($source, $_template); + if (!$source->exists && isset($_template->smarty->default_config_handler_func)) { + Smarty_Internal_Method_RegisterDefaultTemplateHandler::_getDefaultTemplate($source); + } + $source->unique_resource = $resource->buildUniqueResourceName($_template->smarty, $name, true); + return $source; + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_template_resource_base.php b/vendor/smarty/smarty/libs/sysplugins/smarty_template_resource_base.php new file mode 100644 index 00000000000..0911feb8df7 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_template_resource_base.php @@ -0,0 +1,155 @@ +unifunc; + $level = ob_get_level(); + try { + if (empty($unifunc) || !is_callable($unifunc)) { + throw new SmartyException("Invalid compiled template for '{$_template->template_resource}'"); + } + if (isset($_template->smarty->security_policy)) { + $_template->smarty->security_policy->startTemplate($_template); + } + // + // render compiled or saved template code + // + if (!isset($_template->_cache['capture_stack'])) { + $_template->_cache['capture_stack'] = array(); + } + $_saved_capture_level = count($_template->_cache['capture_stack']); + $unifunc($_template); + // any unclosed {capture} tags ? + if ($_saved_capture_level != count($_template->_cache['capture_stack'])) { + $_template->capture_error(); + } + if (isset($_template->smarty->security_policy)) { + $_template->smarty->security_policy->exitTemplate(); + } + return null; + } + catch (Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + if (isset($_template->smarty->security_policy)) { + $_template->smarty->security_policy->exitTemplate(); + } + throw $e; + } + } + + /** + * Get compiled time stamp + * + * @return int + */ + public function getTimeStamp() + { + if ($this->exists && !isset($this->timestamp)) { + $this->timestamp = @filemtime($this->filepath); + } + return $this->timestamp; + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_template_source.php b/vendor/smarty/smarty/libs/sysplugins/smarty_template_source.php new file mode 100644 index 00000000000..be5b628090d --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_template_source.php @@ -0,0 +1,267 @@ +handler = $handler; // Note: prone to circular references + $this->smarty = $smarty; + $this->resource = $resource; + $this->type = $type; + $this->name = $name; + } + + /** + * initialize Source Object for given resource + * Either [$_template] or [$smarty, $template_resource] must be specified + * + * @param Smarty_Internal_Template $_template template object + * @param Smarty $smarty smarty object + * @param string $template_resource resource identifier + * + * @return Smarty_Template_Source Source Object + * @throws SmartyException + */ + public static function load(Smarty_Internal_Template $_template = null, Smarty $smarty = null, + $template_resource = null) + { + if ($_template) { + $smarty = $_template->smarty; + $template_resource = $_template->template_resource; + } + if (empty($template_resource)) { + throw new SmartyException('Missing template name'); + } + // parse resource_name, load resource handler, identify unique resource name + if (preg_match('/^([A-Za-z0-9_\-]{2,})[:]([\s\S]*)$/', $template_resource, $match)) { + $type = $match[1]; + $name = $match[2]; + } else { + // no resource given, use default + // or single character before the colon is not a resource type, but part of the filepath + $type = $smarty->default_resource_type; + $name = $template_resource; + } + + $handler = isset($smarty->_cache['resource_handlers'][$type]) ? + $smarty->_cache['resource_handlers'][$type] : + Smarty_Resource::load($smarty, $type); + // if resource is not recompiling and resource name is not dotted we can check the source cache + if (($smarty->resource_cache_mode & Smarty::RESOURCE_CACHE_ON) && !$handler->recompiled && + !(isset($name[1]) && $name[0] == '.' && ($name[1] == '.' || $name[1] == '/')) + ) { + $unique_resource = $handler->buildUniqueResourceName($smarty, $name); + if (isset($smarty->_cache['source_objects'][$unique_resource])) { + return $smarty->_cache['source_objects'][$unique_resource]; + } + } else { + $unique_resource = null; + } + // create new source object + $source = new Smarty_Template_Source($handler, $smarty, $template_resource, $type, $name); + $handler->populate($source, $_template); + if (!$source->exists && isset($_template->smarty->default_template_handler_func)) { + Smarty_Internal_Method_RegisterDefaultTemplateHandler::_getDefaultTemplate($source); + } + // on recompiling resources we are done + if (($smarty->resource_cache_mode & Smarty::RESOURCE_CACHE_ON) && !$handler->recompiled) { + // may by we have already $unique_resource + $is_relative = false; + if (!isset($unique_resource)) { + $is_relative = isset($name[1]) && $name[0] == '.' && ($name[1] == '.' || $name[1] == '/') && + ($type == 'file' || + (isset($_template->parent->source) && $_template->parent->source->type == 'extends')); + $unique_resource = + $handler->buildUniqueResourceName($smarty, $is_relative ? $source->filepath . $name : $name); + } + $source->unique_resource = $unique_resource; + // save in runtime cache if not relative + if (!$is_relative) { + $smarty->_cache['source_objects'][$unique_resource] = $source; + } + } + return $source; + } + + /** + * render the uncompiled source + * + * @param Smarty_Internal_Template $_template template object + * + * @return string + * @throws \Exception + */ + public function renderUncompiled(Smarty_Internal_Template $_template) + { + $this->handler->renderUncompiled($_template->source, $_template); + } + + /** + * Render uncompiled source + * + * @param \Smarty_Internal_Template $_template + */ + public function render(Smarty_Internal_Template $_template) + { + if ($_template->source->handler->uncompiled) { + if ($_template->smarty->debugging) { + $_template->smarty->_debug->start_render($_template); + } + $this->handler->renderUncompiled($_template->source, $_template); + if (isset($_template->parent) && $_template->parent->_objType == 2 && !empty($_template->tpl_function)) { + $_template->parent->tpl_function = + array_merge($_template->parent->tpl_function, $_template->tpl_function); + } + if ($_template->smarty->debugging) { + $_template->smarty->_debug->end_render($_template); + } + } + } + + /** + * Get source time stamp + * + * @return int + */ + public function getTimeStamp() + { + if (!isset($this->timestamp)) { + $this->handler->populateTimestamp($this); + } + return $this->timestamp; + } + + /** + * Get source content + * + * @return string + */ + public function getContent() + { + return isset($this->content) ? $this->content : $this->handler->getContent($this); + } +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smarty_undefined_variable.php b/vendor/smarty/smarty/libs/sysplugins/smarty_undefined_variable.php new file mode 100644 index 00000000000..88bc6f68c98 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smarty_undefined_variable.php @@ -0,0 +1,37 @@ +value = $value; + $this->nocache = $nocache; + } + + /** + * <> String conversion + * + * @return string + */ + public function __toString() + { + return (string) $this->value; + } +} + diff --git a/vendor/smarty/smarty/libs/sysplugins/smartycompilerexception.php b/vendor/smarty/smarty/libs/sysplugins/smartycompilerexception.php new file mode 100644 index 00000000000..4d5d7787531 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smartycompilerexception.php @@ -0,0 +1,39 @@ + Smarty Compiler: ' . $this->message . ' <-- '; + } + + /** + * The line number of the template error + * + * @type int|null + */ + public $line = null; + /** + * The template source snippet relating to the error + * + * @type string|null + */ + public $source = null; + /** + * The raw text of the error message + * + * @type string|null + */ + public $desc = null; + /** + * The resource identifier or template name + * + * @type string|null + */ + public $template = null; +} diff --git a/vendor/smarty/smarty/libs/sysplugins/smartyexception.php b/vendor/smarty/smarty/libs/sysplugins/smartyexception.php new file mode 100644 index 00000000000..3da16c276c1 --- /dev/null +++ b/vendor/smarty/smarty/libs/sysplugins/smartyexception.php @@ -0,0 +1,15 @@ + Smarty: ' . (self::$escape ? htmlentities($this->message) : $this->message) . ' <-- '; + } +} diff --git a/vendor/symfony/console/.gitignore b/vendor/symfony/console/.gitignore new file mode 100644 index 00000000000..c49a5d8df5c --- /dev/null +++ b/vendor/symfony/console/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php new file mode 100644 index 00000000000..da0f22fd534 --- /dev/null +++ b/vendor/symfony/console/Application.php @@ -0,0 +1,1112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + */ +class Application +{ + private $commands = array(); + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions = true; + private $autoExit = true; + private $definition; + private $helperSet; + private $dispatcher; + private $terminalDimensions; + private $defaultCommand; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->defaultCommand = 'list'; + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if (0 === $exitCode) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--version', '-V'), true)) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(array('--help', '-h'), true)) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $input = new ArrayInput(array('command' => $this->defaultCommand)); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Set an input definition set to be used with this application. + * + * @param InputDefinition $definition The input definition + */ + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message + */ + public function getHelp() + { + return $this->getLongVersion(); + } + + /** + * Gets whether to catch exceptions or not during commands execution. + * + * @return bool Whether to catch exceptions or not during commands execution + */ + public function areExceptionsCaught() + { + return $this->catchExceptions; + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param bool $boolean Whether to catch exceptions or not during commands execution + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (bool) $boolean; + } + + /** + * Gets whether to automatically exit after a command execution or not. + * + * @return bool Whether to automatically exit after a command execution or not + */ + public function isAutoExitEnabled() + { + return $this->autoExit; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param bool $boolean Whether to automatically exit after a command execution or not + */ + public function setAutoExit($boolean) + { + $this->autoExit = (bool) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName()) { + if ('UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } + + return sprintf('%s', $this->getName()); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + if (null === $command->getDefinition()) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws CommandNotFoundException When command name given does not exist + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return bool true if the command exists, false otherwise + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->all() as $command) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); + + foreach ($command->getAliases() as $alias) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws CommandNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = $this->getNamespaces(); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace); + $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); + + if (empty($namespaces)) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + $exact = in_array($namespace, $namespaces, true); + if (count($namespaces) > 1 && !$exact) { + throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find($name) + { + $allCommands = array_keys($this->commands); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + // filter out aliases for commands which are already on the list + if (count($commands) > 1) { + $commandList = $this->commands; + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commandName = $commandList[$nameOrAlias]->getName(); + + return $commandName === $nameOrAlias || !in_array($commandName, $commands); + }); + } + + $exact = in_array($name, $commands, true); + if (count($commands) > 1 && !$exact) { + $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); + + throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands)); + } + + return $this->get($exact ? $name : reset($commands)); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return Command[] An array of Command instances + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + public static function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + /** + * Renders a caught exception. + * + * @param \Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException(\Exception $e, OutputInterface $output) + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + do { + $title = sprintf( + ' [%s%s] ', + get_class($e), + $output->isVerbose() && 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '' + ); + + $len = $this->stringWidth($title); + + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 + if (defined('HHVM_VERSION') && $width > 1 << 31) { + $width = 1 << 31; + } + $formatter = $output->getFormatter(); + $lines = array(); + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4; + $lines[] = array($line, $lineLength); + + $len = max($lineLength, $len); + } + } + + $messages = array(); + $messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len))); + $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))))); + foreach ($lines as $line) { + $messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1]))); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; ++$i) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + /** + * Tries to figure out the terminal width in which this application runs. + * + * @return int|null + */ + protected function getTerminalWidth() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[0]; + } + + /** + * Tries to figure out the terminal height in which this application runs. + * + * @return int|null + */ + protected function getTerminalHeight() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[1]; + } + + /** + * Tries to figure out the terminal dimensions based on the current environment. + * + * @return array Array containing width and height + */ + public function getTerminalDimensions() + { + if ($this->terminalDimensions) { + return $this->terminalDimensions; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + // extract [w, H] from "wxh (WxH)" + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + // extract [w, h] from "wxh" + if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + } + + if ($sttyString = $this->getSttyColumns()) { + // extract [w, h] from "rows h; columns w;" + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + // extract [w, h] from "; h rows; w columns" + if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + } + + return array(null, null); + } + + /** + * Sets terminal dimensions. + * + * Can be useful to force terminal dimensions for functional tests. + * + * @param int $width The width + * @param int $height The height + * + * @return Application The current application + */ + public function setTerminalDimensions($width, $height) + { + $this->terminalDimensions = array($width, $height); + + return $this; + } + + /** + * Configures the input and output instances based on the user arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function configureIO(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--ansi'), true)) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'), true)) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'), true)) { + $input->setInteractive(false); + } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) { + $inputStream = $this->getHelperSet()->get('question')->getInputStream(); + if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'), true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } else { + if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || $input->getParameterOption('--verbose', false, true) === 3) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || $input->getParameterOption('--verbose', false, true) === 2) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + } + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @param Command $command A Command instance + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception when the command being run threw an exception + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface $e) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + + if ($event->commandShouldRun()) { + try { + $exitCode = $command->run($input, $output); + } catch (\Exception $e) { + $event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + + $e = $event->getException(); + + $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + throw $e; + } + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + )); + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output. + * + * @return string + */ + private function getSttyColumns() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output. + * + * @return string x or null if it could not be parsed + */ + private function getConsoleMode() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return $matches[2].'x'.$matches[1]; + } + } + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + public function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @param string $name The string + * @param array|\Traversable $collection The collection + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection) + { + $threshold = 1e3; + $alternatives = array(); + + $collectionParts = array(); + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + asort($alternatives); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @param string $commandName The Command name + */ + public function setDefaultCommand($commandName) + { + $this->defaultCommand = $commandName; + } + + private function stringWidth($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + private function splitStringByWidth($string, $width) + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // additionally, array_slice() is not enough as some character has doubled width. + // we need a function to split string not by character count but by string width + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = array(); + $line = ''; + foreach (preg_split('//u', $utf8String) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + if ('' !== $line) { + $lines[] = count($lines) ? str_pad($line, $width) : $line; + } + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + /** + * Returns all namespaces of the command name. + * + * @param string $name The full name of the command + * + * @return array The namespaces of the command + */ + private function extractAllNamespaces($name) + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = array(); + + foreach ($parts as $part) { + if (count($namespaces)) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } +} diff --git a/vendor/symfony/console/CHANGELOG.md b/vendor/symfony/console/CHANGELOG.md new file mode 100644 index 00000000000..df376403759 --- /dev/null +++ b/vendor/symfony/console/CHANGELOG.md @@ -0,0 +1,79 @@ +CHANGELOG +========= + +3.1.0 +----- + + * added truncate method to FormatterHelper + * added setColumnWidth(s) method to Table + +2.8.3 +----- + + * remove readline support from the question helper as it caused issues + +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + +2.6.0 +----- + + * added a Process helper + * added a DebugFormatter helper + +2.5.0 +----- + + * deprecated the dialog helper (use the question helper instead) + * deprecated TableHelper in favor of Table + * deprecated ProgressHelper in favor of ProgressBar + * added ConsoleLogger + * added a question helper + * added a way to set the process name of a command + * added a way to set a default command instead of `ListCommand` + +2.4.0 +----- + + * added a way to force terminal dimensions + * added a convenient method to detect verbosity level + * [BC BREAK] made descriptors use output instead of returning a string + +2.3.0 +----- + + * added multiselect support to the select dialog helper + * added Table Helper for tabular data rendering + * added support for events in `Application` + * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()` + * added a way to set the progress bar progress via the `setCurrent` method + * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'` + * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/vendor/symfony/console/Command/Command.php b/vendor/symfony/console/Command/Command.php new file mode 100644 index 00000000000..d212a325475 --- /dev/null +++ b/vendor/symfony/console/Command/Command.php @@ -0,0 +1,631 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + */ +class Command +{ + private $application; + private $name; + private $processTitle; + private $aliases = array(); + private $definition; + private $help; + private $description; + private $ignoreValidationErrors = false; + private $applicationDefinitionMerged = false; + private $applicationDefinitionMergedWithArgs = false; + private $code; + private $synopsis = array(); + private $usages = array(); + private $helperSet; + + /** + * Constructor. + * + * @param string|null $name The name of the command; passing null means it must be set in configure() + * + * @throws LogicException When the command name is empty + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment. + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return null|int null or 0 if everything went fine, or an error code + * + * @throws LogicException When this abstract method is not implemented + * + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return int The command exit code + * + * @throws \Exception + * + * @see setCode() + * @see execute() + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(true); + $this->getSynopsis(false); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (ExceptionInterface $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if (null !== $this->processTitle) { + if (function_exists('cli_set_process_title')) { + cli_set_process_title($this->processTitle); + } elseif (function_exists('setproctitle')) { + setproctitle($this->processTitle); + } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Install the proctitle PECL to be able to change the process title.'); + } + } + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + // The command name argument is often omitted when a command is executed directly with its run() method. + // It would fail the validation if we didn't make sure the command argument is present, + // since it's required by the application. + if ($input->hasArgument('command') && null === $input->getArgument('command')) { + $input->setArgument('command', $this->getName()); + } + + $input->validate(); + + if ($this->code) { + $statusCode = call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return Command The current instance + * + * @throws InvalidArgumentException + * + * @see execute() + */ + public function setCode(callable $code) + { + if ($code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + $code = \Closure::bind($code, $this); + } + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * This method is not part of public API and should not be used directly. + * + * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + */ + public function mergeApplicationDefinition($mergeArgs = true) + { + if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + return; + } + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + } + + $this->applicationDefinitionMerged = true; + if ($mergeArgs) { + $this->applicationDefinitionMergedWithArgs = true; + } + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * This method is not part of public API and should not be used directly. + * + * @return InputDefinition An InputDefinition instance + */ + public function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param int $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_NONE) + * + * @return Command The current instance + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws InvalidArgumentException When the name is invalid + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Sets the process title of the command. + * + * This feature should be used only when creating a long process command, + * like a daemon. + * + * PHP 5.5+ or the proctitle PECL library is required + * + * @param string $title The process title + * + * @return Command The current instance + */ + public function setProcessTitle($title) + { + $this->processTitle = $title; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%', + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name, + ); + + return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); + } + + /** + * Sets the aliases for the command. + * + * @param string[] $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @throws InvalidArgumentException When an alias is invalid + */ + public function setAliases($aliases) + { + if (!is_array($aliases) && !$aliases instanceof \Traversable) { + throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); + } + + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @param bool $short Whether to show the short version of the synopsis (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $key = $short ? 'short' : 'long'; + + if (!isset($this->synopsis[$key])) { + $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + } + + return $this->synopsis[$key]; + } + + /** + * Add a command usage example. + * + * @param string $usage The usage, it'll be prefixed with the command name + */ + public function addUsage($usage) + { + if (0 !== strpos($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * Returns alternative usages of the command. + * + * @return array + */ + public function getUsages() + { + return $this->usages; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws LogicException if no HelperSet is defined + * @throws InvalidArgumentException if the helper is not defined + */ + public function getHelper($name) + { + if (null === $this->helperSet) { + throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); + } + + return $this->helperSet->get($name); + } + + /** + * Validates a command name. + * + * It must be non-empty and parts can optionally be separated by ":". + * + * @param string $name + * + * @throws InvalidArgumentException When the name is invalid + */ + private function validateName($name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/symfony/console/Command/HelpCommand.php b/vendor/symfony/console/Command/HelpCommand.php new file mode 100644 index 00000000000..b8fd911ad40 --- /dev/null +++ b/vendor/symfony/console/Command/HelpCommand.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<<'EOF' +The %command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help in other formats by using the --format option: + + php %command.full_name% --format=xml list + +To display the list of available commands, please use the list command. +EOF + ) + ; + } + + /** + * Sets the command. + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->find($input->getArgument('command_name')); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + )); + + $this->command = null; + } +} diff --git a/vendor/symfony/console/Command/ListCommand.php b/vendor/symfony/console/Command/ListCommand.php new file mode 100644 index 00000000000..179ddea5dc2 --- /dev/null +++ b/vendor/symfony/console/Command/ListCommand.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 Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +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 Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<<'EOF' +The %command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information in other formats by using the --format option: + + php %command.full_name% --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + public function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + )); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + )); + } +} diff --git a/vendor/symfony/console/ConsoleEvents.php b/vendor/symfony/console/ConsoleEvents.php new file mode 100644 index 00000000000..b3571e9afb3 --- /dev/null +++ b/vendor/symfony/console/ConsoleEvents.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +/** + * Contains all events dispatched by an Application. + * + * @author Francesco Levorato + */ +final class ConsoleEvents +{ + /** + * The COMMAND event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the command, input and output + * before they are handled to the command. + * + * @Event("Symfony\Component\Console\Event\ConsoleCommandEvent") + * + * @var string + */ + const COMMAND = 'console.command'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * @Event("Symfony\Component\Console\Event\ConsoleTerminateEvent") + * + * @var string + */ + const TERMINATE = 'console.terminate'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears. + * + * This event allows you to deal with the exception or + * to modify the thrown exception. + * + * @Event("Symfony\Component\Console\Event\ConsoleExceptionEvent") + * + * @var string + */ + const EXCEPTION = 'console.exception'; +} diff --git a/vendor/symfony/console/Descriptor/ApplicationDescription.php b/vendor/symfony/console/Descriptor/ApplicationDescription.php new file mode 100644 index 00000000000..89961b9cae7 --- /dev/null +++ b/vendor/symfony/console/Descriptor/ApplicationDescription.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 Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Jean-François Simon + * + * @internal + */ +class ApplicationDescription +{ + const GLOBAL_NAMESPACE = '_global'; + + /** + * @var Application + */ + private $application; + + /** + * @var null|string + */ + private $namespace; + + /** + * @var array + */ + private $namespaces; + + /** + * @var Command[] + */ + private $commands; + + /** + * @var Command[] + */ + private $aliases; + + /** + * Constructor. + * + * @param Application $application + * @param string|null $namespace + */ + public function __construct(Application $application, $namespace = null) + { + $this->application = $application; + $this->namespace = $namespace; + } + + /** + * @return array + */ + public function getNamespaces() + { + if (null === $this->namespaces) { + $this->inspectApplication(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands() + { + if (null === $this->commands) { + $this->inspectApplication(); + } + + return $this->commands; + } + + /** + * @param string $name + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name)); + } + + return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + } + + private function inspectApplication() + { + $this->commands = array(); + $this->namespaces = array(); + + $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = array(); + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName()) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names); + } + } + + /** + * @param array $commands + * + * @return array + */ + private function sortCommands(array $commands) + { + $namespacedCommands = array(); + $globalCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (!$key) { + $globalCommands['_global'][$name] = $command; + } else { + $namespacedCommands[$key][$name] = $command; + } + } + ksort($namespacedCommands); + $namespacedCommands = array_merge($globalCommands, $namespacedCommands); + + foreach ($namespacedCommands as &$commandsSet) { + ksort($commandsSet); + } + // unset reference to keep scope clear + unset($commandsSet); + + return $namespacedCommands; + } +} diff --git a/vendor/symfony/console/Descriptor/Descriptor.php b/vendor/symfony/console/Descriptor/Descriptor.php new file mode 100644 index 00000000000..50dd86ce23f --- /dev/null +++ b/vendor/symfony/console/Descriptor/Descriptor.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $this->output = $output; + + switch (true) { + case $object instanceof InputArgument: + $this->describeInputArgument($object, $options); + break; + case $object instanceof InputOption: + $this->describeInputOption($object, $options); + break; + case $object instanceof InputDefinition: + $this->describeInputDefinition($object, $options); + break; + case $object instanceof Command: + $this->describeCommand($object, $options); + break; + case $object instanceof Application: + $this->describeApplication($object, $options); + break; + default: + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + * + * @param InputArgument $argument + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = array()); + + /** + * Describes an InputOption instance. + * + * @param InputOption $option + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputOption(InputOption $option, array $options = array()); + + /** + * Describes an InputDefinition instance. + * + * @param InputDefinition $definition + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array()); + + /** + * Describes a Command instance. + * + * @param Command $command + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeCommand(Command $command, array $options = array()); + + /** + * Describes an Application instance. + * + * @param Application $application + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeApplication(Application $application, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/DescriptorInterface.php b/vendor/symfony/console/Descriptor/DescriptorInterface.php new file mode 100644 index 00000000000..3929b6d9ed7 --- /dev/null +++ b/vendor/symfony/console/Descriptor/DescriptorInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + /** + * Describes an InputArgument instance. + * + * @param OutputInterface $output + * @param object $object + * @param array $options + */ + public function describe(OutputInterface $output, $object, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/JsonDescriptor.php b/vendor/symfony/console/Descriptor/JsonDescriptor.php new file mode 100644 index 00000000000..87e38fdb890 --- /dev/null +++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * JSON descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeData($this->getInputOptionData($option), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeData($this->getCommandData($command), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $commands = array(); + + foreach ($description->getCommands() as $command) { + $commands[] = $this->getCommandData($command); + } + + $data = $describedNamespace + ? array('commands' => $commands, 'namespace' => $describedNamespace) + : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces())); + + $this->writeData($data, $options); + } + + /** + * Writes data as json. + * + * @param array $data + * @param array $options + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0)); + } + + /** + * @param InputArgument $argument + * + * @return array + */ + private function getInputArgumentData(InputArgument $argument) + { + return array( + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), + 'default' => $argument->getDefault(), + ); + } + + /** + * @param InputOption $option + * + * @return array + */ + private function getInputOptionData(InputOption $option) + { + return array( + 'name' => '--'.$option->getName(), + 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '', + 'accept_value' => $option->acceptValue(), + 'is_value_required' => $option->isValueRequired(), + 'is_multiple' => $option->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), + 'default' => $option->getDefault(), + ); + } + + /** + * @param InputDefinition $definition + * + * @return array + */ + private function getInputDefinitionData(InputDefinition $definition) + { + $inputArguments = array(); + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->getInputArgumentData($argument); + } + + $inputOptions = array(); + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->getInputOptionData($option); + } + + return array('arguments' => $inputArguments, 'options' => $inputOptions); + } + + /** + * @param Command $command + * + * @return array + */ + private function getCommandData(Command $command) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + return array( + 'name' => $command->getName(), + 'usage' => array_merge(array($command->getSynopsis()), $command->getUsages(), $command->getAliases()), + 'description' => $command->getDescription(), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), + ); + } +} diff --git a/vendor/symfony/console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 00000000000..d3d76a42010 --- /dev/null +++ b/vendor/symfony/console/Descriptor/MarkdownDescriptor.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 Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Markdown descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->write( + '**'.$argument->getName().':**'."\n\n" + .'* Name: '.($argument->getName() ?: '')."\n" + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $argument->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->write( + '**'.$option->getName().':**'."\n\n" + .'* Name: `--'.$option->getName().'`'."\n" + .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '')."\n" + .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" + .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" + .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $option->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + if ($showArguments = count($definition->getArguments()) > 0) { + $this->write('### Arguments:'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->write($this->describeInputArgument($argument)); + } + } + + if (count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options:'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + $this->write($this->describeInputOption($option)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $this->write( + $command->getName()."\n" + .str_repeat('-', strlen($command->getName()))."\n\n" + .'* Description: '.($command->getDescription() ?: '')."\n" + .'* Usage:'."\n\n" + .array_reduce(array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry .= ' * `'.$usage.'`'."\n"; + }) + ); + + if ($help = $command->getProcessedHelp()) { + $this->write("\n"); + $this->write($help); + } + + if ($command->getNativeDefinition()) { + $this->write("\n\n"); + $this->describeInputDefinition($command->getNativeDefinition()); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + $this->write($application->getName()."\n".str_repeat('=', strlen($application->getName()))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->write("\n\n"); + $this->write('**'.$namespace['id'].':**'); + } + + $this->write("\n\n"); + $this->write(implode("\n", array_map(function ($commandName) { + return '* '.$commandName; + }, $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + $this->write($this->describeCommand($command)); + } + } +} diff --git a/vendor/symfony/console/Descriptor/TextDescriptor.php b/vendor/symfony/console/Descriptor/TextDescriptor.php new file mode 100644 index 00000000000..560ce150e26 --- /dev/null +++ b/vendor/symfony/console/Descriptor/TextDescriptor.php @@ -0,0 +1,283 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Text descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); + $spacingWidth = $totalWidth - strlen($argument->getName()) + 2; + + $this->writeText(sprintf(' %s%s%s%s', + $argument->getName(), + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $argument->getDescription()), + $default + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $value = ''; + if ($option->acceptValue()) { + $value = '='.strtoupper($option->getName()); + + if ($option->isValueOptional()) { + $value = '['.$value.']'; + } + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option)); + $synopsis = sprintf('%s%s', + $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', + sprintf('--%s%s', $option->getName(), $value) + ); + + $spacingWidth = $totalWidth - strlen($synopsis) + 2; + + $this->writeText(sprintf(' %s%s%s%s%s', + $synopsis, + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $option->getDescription()), + $default, + $option->isArray() ? ' (multiple values allowed)' : '' + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, strlen($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('Arguments:', $options); + $this->writeText("\n"); + foreach ($definition->getArguments() as $argument) { + $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth))); + $this->writeText("\n"); + } + } + + if ($definition->getArguments() && $definition->getOptions()) { + $this->writeText("\n"); + } + + if ($definition->getOptions()) { + $laterOptions = array(); + + $this->writeText('Options:', $options); + foreach ($definition->getOptions() as $option) { + if (strlen($option->getShortcut()) > 1) { + $laterOptions[] = $option; + continue; + } + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + foreach ($laterOptions as $option) { + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(true); + $command->getSynopsis(false); + $command->mergeApplicationDefinition(false); + + $this->writeText('Usage:', $options); + foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' '.$usage, $options); + } + $this->writeText("\n"); + + $definition = $command->getNativeDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + if ($help = $command->getProcessedHelp()) { + $this->writeText("\n"); + $this->writeText('Help:', $options); + $this->writeText("\n"); + $this->writeText(' '.str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $application->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("Usage:\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $width = $this->getColumnWidth($description->getCommands()); + + if ($describedNamespace) { + $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + } else { + $this->writeText('Available commands:', $options); + } + + // add commands by namespace + foreach ($description->getNamespaces() as $namespace) { + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' '.$namespace['id'].'', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - strlen($name); + $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText($content, array $options = array()) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } + + /** + * Formats input option/argument default value. + * + * @param mixed $default + * + * @return string + */ + private function formatDefaultValue($default) + { + return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + /** + * @param Command[] $commands + * + * @return int + */ + private function getColumnWidth(array $commands) + { + $widths = array(); + + foreach ($commands as $command) { + $widths[] = strlen($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = strlen($alias); + } + } + + return max($widths) + 2; + } + + /** + * @param InputOption[] $options + * + * @return int + */ + private function calculateTotalWidthForOptions($options) + { + $totalWidth = 0; + foreach ($options as $option) { + // "-" + shortcut + ", --" + name + $nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName()); + + if ($option->acceptValue()) { + $valueLength = 1 + strlen($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/vendor/symfony/console/Descriptor/XmlDescriptor.php b/vendor/symfony/console/Descriptor/XmlDescriptor.php new file mode 100644 index 00000000000..b5676beb376 --- /dev/null +++ b/vendor/symfony/console/Descriptor/XmlDescriptor.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * XML descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * @param InputDefinition $definition + * + * @return \DOMDocument + */ + public function getInputDefinitionDocument(InputDefinition $definition) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($definition->getArguments() as $argument) { + $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument)); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } + + return $dom; + } + + /** + * @param Command $command + * + * @return \DOMDocument + */ + public function getCommandDocument(Command $command) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($commandXML = $dom->createElement('command')); + + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $commandXML->setAttribute('id', $command->getName()); + $commandXML->setAttribute('name', $command->getName()); + + $commandXML->appendChild($usagesXML = $dom->createElement('usages')); + + foreach (array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + + return $dom; + } + + /** + * @param Application $application + * @param string|null $namespace + * + * @return \DOMDocument + */ + public function getApplicationDocument(Application $application, $namespace = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + + if ($application->getName() !== 'UNKNOWN') { + $rootXml->setAttribute('name', $application->getName()); + if ($application->getVersion() !== 'UNKNOWN') { + $rootXml->setAttribute('version', $application->getVersion()); + } + } + + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $description = new ApplicationDescription($application, $namespace); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->getCommandDocument($command)); + } + + if (!$namespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespaceDescription) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']); + + foreach ($namespaceDescription['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $dom; + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeDocument($this->getCommandDocument($command)); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); + } + + /** + * Appends document children to parent node. + * + * @param \DOMNode $parentNode + * @param \DOMNode $importedParent + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + * + * @param \DOMDocument $dom + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @param InputArgument $argument + * + * @return \DOMDocument + */ + private function getInputArgumentDocument(InputArgument $argument) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('argument')); + $objectXML->setAttribute('name', $argument->getName()); + $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + + return $dom; + } + + /** + * @param InputOption $option + * + * @return \DOMDocument + */ + private function getInputOptionDocument(InputOption $option) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--'.$option->getName()); + $pos = strpos($option->getShortcut(), '|'); + if (false !== $pos) { + $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); + $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut()))); + } else { + $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + } + $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + + if (!empty($defaults)) { + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $dom; + } +} diff --git a/vendor/symfony/console/Event/ConsoleCommandEvent.php b/vendor/symfony/console/Event/ConsoleCommandEvent.php new file mode 100644 index 00000000000..92adf1ef96c --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleCommandEvent.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or changing the input. + * + * @author Fabien Potencier + */ +class ConsoleCommandEvent extends ConsoleEvent +{ + /** + * The return code for skipped commands, this will also be passed into the terminate event. + */ + const RETURN_CODE_DISABLED = 113; + + /** + * Indicates if the command should be run or skipped. + * + * @var bool + */ + private $commandShouldRun = true; + + /** + * Disables the command, so it won't be run. + * + * @return bool + */ + public function disableCommand() + { + return $this->commandShouldRun = false; + } + + /** + * Enables the command. + * + * @return bool + */ + public function enableCommand() + { + return $this->commandShouldRun = true; + } + + /** + * Returns true if the command is runnable, false otherwise. + * + * @return bool + */ + public function commandShouldRun() + { + return $this->commandShouldRun; + } +} diff --git a/vendor/symfony/console/Event/ConsoleEvent.php b/vendor/symfony/console/Event/ConsoleEvent.php new file mode 100644 index 00000000000..ab620c4609a --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleEvent.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 Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato + */ +class ConsoleEvent extends Event +{ + protected $command; + + private $input; + private $output; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output) + { + $this->command = $command; + $this->input = $input; + $this->output = $output; + } + + /** + * Gets the command that is executed. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * Gets the input instance. + * + * @return InputInterface An InputInterface instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance. + * + * @return OutputInterface An OutputInterface instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/vendor/symfony/console/Event/ConsoleExceptionEvent.php b/vendor/symfony/console/Event/ConsoleExceptionEvent.php new file mode 100644 index 00000000000..603b7eed78c --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleExceptionEvent.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 Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle exception thrown in a command. + * + * @author Fabien Potencier + */ +class ConsoleExceptionEvent extends ConsoleEvent +{ + private $exception; + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setException($exception); + $this->exitCode = (int) $exitCode; + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/vendor/symfony/console/Event/ConsoleTerminateEvent.php new file mode 100644 index 00000000000..b6a5d7c0dc4 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleTerminateEvent.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 Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to manipulate the exit code of a command after its execution. + * + * @author Francesco Levorato + */ +class ConsoleTerminateEvent extends ConsoleEvent +{ + /** + * The exit code of the command. + * + * @var int + */ + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setExitCode($exitCode); + } + + /** + * Sets the exit code. + * + * @param int $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = (int) $exitCode; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Exception/CommandNotFoundException.php b/vendor/symfony/console/Exception/CommandNotFoundException.php new file mode 100644 index 00000000000..54f1a5b0ce8 --- /dev/null +++ b/vendor/symfony/console/Exception/CommandNotFoundException.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect command name typed in the console. + * + * @author Jérôme Tamarelle + */ +class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ + private $alternatives; + + /** + * @param string $message Exception message to throw + * @param array $alternatives List of similar defined names + * @param int $code Exception code + * @param Exception $previous previous exception used for the exception chaining + */ + public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->alternatives = $alternatives; + } + + /** + * @return array A list of similar defined names + */ + public function getAlternatives() + { + return $this->alternatives; + } +} diff --git a/vendor/symfony/console/Exception/ExceptionInterface.php b/vendor/symfony/console/Exception/ExceptionInterface.php new file mode 100644 index 00000000000..491cc4c6456 --- /dev/null +++ b/vendor/symfony/console/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * ExceptionInterface. + * + * @author Jérôme Tamarelle + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidArgumentException.php b/vendor/symfony/console/Exception/InvalidArgumentException.php new file mode 100644 index 00000000000..07cc0b61d6d --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidOptionException.php b/vendor/symfony/console/Exception/InvalidOptionException.php new file mode 100644 index 00000000000..b2eec61658d --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidOptionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect option name typed in the console. + * + * @author Jérôme Tamarelle + */ +class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/LogicException.php b/vendor/symfony/console/Exception/LogicException.php new file mode 100644 index 00000000000..fc37b8d8ae4 --- /dev/null +++ b/vendor/symfony/console/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/RuntimeException.php b/vendor/symfony/console/Exception/RuntimeException.php new file mode 100644 index 00000000000..51d7d80ac65 --- /dev/null +++ b/vendor/symfony/console/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Formatter/OutputFormatter.php b/vendor/symfony/console/Formatter/OutputFormatter.php new file mode 100644 index 00000000000..56cd5e568f1 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatter.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + */ +class OutputFormatter implements OutputFormatterInterface +{ + private $decorated; + private $styles = array(); + private $styleStack; + + /** + * Escapes "<" special char in given text. + * + * @param string $text Text to escape + * + * @return string Escaped text + */ + public static function escape($text) + { + $text = preg_replace('/([^\\\\]?) FormatterStyle" instances + */ + public function __construct($decorated = false, array $styles = array()) + { + $this->decorated = (bool) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated) + { + $this->decorated = (bool) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style isn't defined + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: %s', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message) + { + $message = (string) $message; + $offset = 0; + $output = ''; + $tagRegex = '[a-z][a-z0-9_=;-]*+'; + preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + // add the text up to the next tag + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); + $offset = $pos + strlen($text); + + // opening tag? + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + } + + if (!$open && !$tag) { + // + $this->styleStack->pop(); + } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { + $output .= $this->applyCurrentStyle($text); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset)); + + if (false !== strpos($output, '<<')) { + return strtr($output, array('\\<' => '<', '<<' => '\\')); + } + + return str_replace('\\<', '<', $output); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|bool false if string is not format string + */ + private function createStyleFromString($string) + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + try { + $style->setOption($match[1]); + } catch (\InvalidArgumentException $e) { + return false; + } + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + * + * @param string $text Input text + * + * @return string Styled text + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Formatter/OutputFormatterInterface.php new file mode 100644 index 00000000000..5a52ba096b2 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php new file mode 100644 index 00000000000..c7c6b4a0191 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = array( + 'black' => array('set' => 30, 'unset' => 39), + 'red' => array('set' => 31, 'unset' => 39), + 'green' => array('set' => 32, 'unset' => 39), + 'yellow' => array('set' => 33, 'unset' => 39), + 'blue' => array('set' => 34, 'unset' => 39), + 'magenta' => array('set' => 35, 'unset' => 39), + 'cyan' => array('set' => 36, 'unset' => 39), + 'white' => array('set' => 37, 'unset' => 39), + 'default' => array('set' => 39, 'unset' => 39), + ); + private static $availableBackgroundColors = array( + 'black' => array('set' => 40, 'unset' => 49), + 'red' => array('set' => 41, 'unset' => 49), + 'green' => array('set' => 42, 'unset' => 49), + 'yellow' => array('set' => 43, 'unset' => 49), + 'blue' => array('set' => 44, 'unset' => 49), + 'magenta' => array('set' => 45, 'unset' => 49), + 'cyan' => array('set' => 46, 'unset' => 49), + 'white' => array('set' => 47, 'unset' => 49), + 'default' => array('set' => 49, 'unset' => 49), + ); + private static $availableOptions = array( + 'bold' => array('set' => 1, 'unset' => 22), + 'underscore' => array('set' => 4, 'unset' => 24), + 'blink' => array('set' => 5, 'unset' => 25), + 'reverse' => array('set' => 7, 'unset' => 27), + 'conceal' => array('set' => 8, 'unset' => 28), + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string|null $foreground The style foreground color name + * @param string|null $background The style background color name + * @param array $options The style options + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (!in_array(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $setCodes = array(); + $unsetCodes = array(); + + if (null !== $this->foreground) { + $setCodes[] = $this->foreground['set']; + $unsetCodes[] = $this->foreground['unset']; + } + if (null !== $this->background) { + $setCodes[] = $this->background['set']; + $unsetCodes[] = $this->background['unset']; + } + if (count($this->options)) { + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + $unsetCodes[] = $option['unset']; + } + } + + if (0 === count($setCodes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 00000000000..c36fda80708 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color The color name + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + */ + public function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option The option name + */ + public function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 00000000000..e5d14ea3fb0 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + /** + * @var OutputFormatterStyleInterface + */ + private $emptyStyle; + + /** + * Constructor. + * + * @param OutputFormatterStyleInterface|null $emptyStyle + */ + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = array(); + } + + /** + * Pushes a style in the stack. + * + * @param OutputFormatterStyleInterface $style + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @param OutputFormatterStyleInterface|null $style + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[count($this->styles) - 1]; + } + + /** + * @param OutputFormatterStyleInterface $emptyStyle + * + * @return OutputFormatterStyleStack + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return OutputFormatterStyleInterface + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/vendor/symfony/console/Helper/DebugFormatterHelper.php b/vendor/symfony/console/Helper/DebugFormatterHelper.php new file mode 100644 index 00000000000..1119b795c8b --- /dev/null +++ b/vendor/symfony/console/Helper/DebugFormatterHelper.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 Symfony\Component\Console\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier + */ +class DebugFormatterHelper extends Helper +{ + private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'); + private $started = array(); + private $count = -1; + + /** + * Starts a debug formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param string $prefix The prefix to use + * + * @return string + */ + public function start($id, $message, $prefix = 'RUN') + { + $this->started[$id] = array('border' => ++$this->count % count($this->colors)); + + return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + } + + /** + * Adds progress to a formatting session. + * + * @param string $id The id of the formatting session + * @param string $buffer The message to display + * @param bool $error Whether to consider the buffer as error + * @param string $prefix The prefix for output + * @param string $errorPrefix The prefix for error output + * + * @return string + */ + public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + { + $message = ''; + + if ($error) { + if (isset($this->started[$id]['out'])) { + $message .= "\n"; + unset($this->started[$id]['out']); + } + if (!isset($this->started[$id]['err'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (isset($this->started[$id]['err'])) { + $message .= "\n"; + unset($this->started[$id]['err']); + } + if (!isset($this->started[$id]['out'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + /** + * Stops a formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param bool $successful Whether to consider the result as success + * @param string $prefix The prefix for the end output + * + * @return string + */ + public function stop($id, $message, $successful, $prefix = 'RES') + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + + unset($this->started[$id]['out'], $this->started[$id]['err']); + + return $message; + } + + /** + * @param string $id The id of the formatting session + * + * @return string + */ + private function getBorder($id) + { + return sprintf(' ', $this->colors[$this->started[$id]['border']]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'debug_formatter'; + } +} diff --git a/vendor/symfony/console/Helper/DescriptorHelper.php b/vendor/symfony/console/Helper/DescriptorHelper.php new file mode 100644 index 00000000000..a53b476b17c --- /dev/null +++ b/vendor/symfony/console/Helper/DescriptorHelper.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * This class adds helper method to describe objects in various formats. + * + * @author Jean-François Simon + */ +class DescriptorHelper extends Helper +{ + /** + * @var DescriptorInterface[] + */ + private $descriptors = array(); + + /** + * Constructor. + */ + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @param OutputInterface $output + * @param object $object + * @param array $options + * + * @throws InvalidArgumentException when the given format is not supported + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $options = array_merge(array( + 'raw_text' => false, + 'format' => 'txt', + ), $options); + + if (!isset($this->descriptors[$options['format']])) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + $descriptor->describe($output, $object, $options); + } + + /** + * Registers a descriptor. + * + * @param string $format + * @param DescriptorInterface $descriptor + * + * @return DescriptorHelper + */ + public function register($format, DescriptorInterface $descriptor) + { + $this->descriptors[$format] = $descriptor; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'descriptor'; + } +} diff --git a/vendor/symfony/console/Helper/FormatterHelper.php b/vendor/symfony/console/Helper/FormatterHelper.php new file mode 100644 index 00000000000..6a48a77f269 --- /dev/null +++ b/vendor/symfony/console/Helper/FormatterHelper.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + * + * @return string The format section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param bool $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + if (!is_array($messages)) { + $messages = array($messages); + } + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + for ($i = 0; isset($lines[$i]); ++$i) { + $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + for ($i = 0; isset($messages[$i]); ++$i) { + $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); + } + + return implode("\n", $messages); + } + + /** + * Truncates a message to the given length. + * + * @param string $message + * @param int $length + * @param string $suffix + * + * @return string + */ + public function truncate($message, $length, $suffix = '...') + { + $computedLength = $length - $this->strlen($suffix); + + if ($computedLength > $this->strlen($message)) { + return $message; + } + + if (false === $encoding = mb_detect_encoding($message, null, true)) { + return substr($message, 0, $length).$suffix; + } + + return mb_substr($message, 0, $length, $encoding).$suffix; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/vendor/symfony/console/Helper/Helper.php b/vendor/symfony/console/Helper/Helper.php new file mode 100644 index 00000000000..43f9a169461 --- /dev/null +++ b/vendor/symfony/console/Helper/Helper.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 Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Returns the length of a string, using mb_strwidth if it is available. + * + * @param string $string The string to check its length + * + * @return int The length of the string + */ + public static function strlen($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + public static function formatTime($secs) + { + static $timeFormats = array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, 'secs', 1), + array(60, '1 min'), + array(120, 'mins', 60), + array(3600, '1 hr'), + array(7200, 'hrs', 3600), + array(86400, '1 day'), + array(172800, 'days', 86400), + ); + + foreach ($timeFormats as $index => $format) { + if ($secs >= $format[0]) { + if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) + || $index == count($timeFormats) - 1 + ) { + if (2 == count($format)) { + return $format[1]; + } + + return floor($secs / $format[2]).' '.$format[1]; + } + } + } + } + + public static function formatMemory($memory) + { + if ($memory >= 1024 * 1024 * 1024) { + return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + } + + if ($memory >= 1024 * 1024) { + return sprintf('%.1f MiB', $memory / 1024 / 1024); + } + + if ($memory >= 1024) { + return sprintf('%d KiB', $memory / 1024); + } + + return sprintf('%d B', $memory); + } + + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + { + $isDecorated = $formatter->isDecorated(); + $formatter->setDecorated(false); + // remove <...> formatting + $string = $formatter->format($string); + // remove already formatted characters + $string = preg_replace("/\033\[[^m]*m/", '', $string); + $formatter->setDecorated($isDecorated); + + return self::strlen($string); + } +} diff --git a/vendor/symfony/console/Helper/HelperInterface.php b/vendor/symfony/console/Helper/HelperInterface.php new file mode 100644 index 00000000000..5a923e0a88c --- /dev/null +++ b/vendor/symfony/console/Helper/HelperInterface.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 Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName(); +} diff --git a/vendor/symfony/console/Helper/HelperSet.php b/vendor/symfony/console/Helper/HelperSet.php new file mode 100644 index 00000000000..6f12b39d98c --- /dev/null +++ b/vendor/symfony/console/Helper/HelperSet.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet implements \IteratorAggregate +{ + /** + * @var Helper[] + */ + private $helpers = array(); + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper + */ + public function __construct(array $helpers = array()) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return bool true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * @return Helper[] + */ + public function getIterator() + { + return new \ArrayIterator($this->helpers); + } +} diff --git a/vendor/symfony/console/Helper/InputAwareHelper.php b/vendor/symfony/console/Helper/InputAwareHelper.php new file mode 100644 index 00000000000..4261767423b --- /dev/null +++ b/vendor/symfony/console/Helper/InputAwareHelper.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 Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputAwareInterface; + +/** + * An implementation of InputAwareInterface for Helpers. + * + * @author Wouter J + */ +abstract class InputAwareHelper extends Helper implements InputAwareInterface +{ + protected $input; + + /** + * {@inheritdoc} + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/vendor/symfony/console/Helper/ProcessHelper.php b/vendor/symfony/console/Helper/ProcessHelper.php new file mode 100644 index 00000000000..2c46a2c39d0 --- /dev/null +++ b/vendor/symfony/console/Helper/ProcessHelper.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 Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessBuilder; + +/** + * The ProcessHelper class provides helpers to run external processes. + * + * @author Fabien Potencier + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * @param int $verbosity The threshold for verbosity + * + * @return Process The process that ran + */ + public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + if (is_array($cmd)) { + $process = ProcessBuilder::create($cmd)->getProcess(); + } elseif ($cmd instanceof Process) { + $process = $cmd; + } else { + $process = new Process($cmd); + } + + if ($verbosity <= $output->getVerbosity()) { + $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); + } + + if ($output->isDebug()) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback); + + if ($verbosity <= $output->getVerbosity()) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('%s', $this->escapeString($error))); + } + + return $process; + } + + /** + * Runs the process. + * + * This is identical to run() except that an exception is thrown if the process + * exits with a non-zero exit code. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|Process $cmd An instance of Process or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process that ran + * + * @throws ProcessFailedException + * + * @see run() + */ + public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null) + { + $process = $this->run($output, $cmd, $error, $callback); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + * + * @param OutputInterface $output An OutputInterface interface + * @param Process $process The Process + * @param callable|null $callback A PHP callable + * + * @return callable + */ + public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + return function ($type, $buffer) use ($output, $process, $callback, $formatter) { + $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); + + if (null !== $callback) { + call_user_func($callback, $type, $buffer); + } + }; + } + + private function escapeString($str) + { + return str_replace('<', '\\<', $str); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'process'; + } +} diff --git a/vendor/symfony/console/Helper/ProgressBar.php b/vendor/symfony/console/Helper/ProgressBar.php new file mode 100644 index 00000000000..6aea12ea4d6 --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressBar.php @@ -0,0 +1,600 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\LogicException; + +/** + * The ProgressBar provides helpers to display progress output. + * + * @author Fabien Potencier + * @author Chris Jones + */ +class ProgressBar +{ + // options + private $barWidth = 28; + private $barChar; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format; + private $internalFormat; + private $redrawFreq = 1; + + /** + * @var OutputInterface + */ + private $output; + private $step = 0; + private $max; + private $startTime; + private $stepWidth; + private $percent = 0.0; + private $formatLineCount; + private $messages = array(); + private $overwrite = true; + private $firstRun = true; + + private static $formatters; + private static $formats; + + /** + * Constructor. + * + * @param OutputInterface $output An OutputInterface instance + * @param int $max Maximum steps (0 if unknown) + */ + public function __construct(OutputInterface $output, $max = 0) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->output = $output; + $this->setMaxSteps($max); + + if (!$this->output->isDecorated()) { + // disable overwrite when output does not support ANSI codes. + $this->overwrite = false; + + // set a reasonable redraw frequency so output isn't flooded + $this->setRedrawFrequency($max / 10); + } + + $this->startTime = time(); + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, callable $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + /** + * Sets a format for a given name. + * + * This method also allow you to override an existing format. + * + * @param string $name The format name + * @param string $format A format string + */ + public static function setFormatDefinition($name, $format) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + self::$formats[$name] = $format; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Associates a text with a named placeholder. + * + * The text is displayed when the progress bar is rendered but only + * when the corresponding placeholder is part of the custom format line + * (by wrapping the name with %). + * + * @param string $message The text to associate with the placeholder + * @param string $name The name of the placeholder + */ + public function setMessage($message, $name = 'message') + { + $this->messages[$name] = $message; + } + + public function getMessage($name = 'message') + { + return $this->messages[$name]; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the progress bar maximal steps. + * + * @return int The progress bar max steps + */ + public function getMaxSteps() + { + return $this->max; + } + + /** + * Gets the current step position. + * + * @return int The progress bar step + */ + public function getProgress() + { + return $this->step; + } + + /** + * Gets the progress bar step width. + * + * @return int The progress bar step width + */ + private function getStepWidth() + { + return $this->stepWidth; + } + + /** + * Gets the current progress bar percent. + * + * @return float The current progress bar percent + */ + public function getProgressPercent() + { + return $this->percent; + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Gets the progress bar width. + * + * @return int The progress bar size + */ + public function getBarWidth() + { + return $this->barWidth; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Gets the bar character. + * + * @return string A character + */ + public function getBarCharacter() + { + if (null === $this->barChar) { + return $this->max ? '=' : $this->emptyBarChar; + } + + return $this->barChar; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Gets the empty bar character. + * + * @return string A character + */ + public function getEmptyBarCharacter() + { + return $this->emptyBarChar; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Gets the progress bar character. + * + * @return string A character + */ + public function getProgressCharacter() + { + return $this->progressChar; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|float $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = max((int) $freq, 1); + } + + /** + * Starts the progress output. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged + */ + public function start($max = null) + { + $this->startTime = time(); + $this->step = 0; + $this->percent = 0.0; + + if (null !== $max) { + $this->setMaxSteps($max); + } + + $this->display(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + * + * @throws LogicException + */ + public function advance($step = 1) + { + $this->setProgress($this->step + $step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + * + * @param bool $overwrite + */ + public function setOverwrite($overwrite) + { + $this->overwrite = (bool) $overwrite; + } + + /** + * Sets the current progress. + * + * @param int $step The current progress + * + * @throws LogicException + */ + public function setProgress($step) + { + $step = (int) $step; + if ($step < $this->step) { + throw new LogicException('You can\'t regress the progress bar.'); + } + + if ($this->max && $step > $this->max) { + $this->max = $step; + } + + $prevPeriod = (int) ($this->step / $this->redrawFreq); + $currPeriod = (int) ($step / $this->redrawFreq); + $this->step = $step; + $this->percent = $this->max ? (float) $this->step / $this->max : 0; + if ($prevPeriod !== $currPeriod || $this->max === $step) { + $this->display(); + } + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (!$this->max) { + $this->max = $this->step; + } + + if ($this->step === $this->max && !$this->overwrite) { + // prevent double 100% output + return; + } + + $this->setProgress($this->max); + } + + /** + * Outputs the current progress string. + */ + public function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) { + if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { + $text = call_user_func($formatter, $this, $this->output); + } elseif (isset($this->messages[$matches[1]])) { + $text = $this->messages[$matches[1]]; + } else { + return $matches[0]; + } + + if (isset($matches[2])) { + $text = sprintf('%'.$matches[2], $text); + } + + return $text; + }, $this->format)); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + if (!$this->overwrite) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(''); + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + private function setRealFormat($format) + { + // try to use the _nomax variant if available + if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { + $this->format = self::getFormatDefinition($format.'_nomax'); + } elseif (null !== self::getFormatDefinition($format)) { + $this->format = self::getFormatDefinition($format); + } else { + $this->format = $format; + } + + $this->formatLineCount = substr_count($this->format, "\n"); + } + + /** + * Sets the progress bar maximal steps. + * + * @param int $max The progress bar max steps + */ + private function setMaxSteps($max) + { + $this->max = max(0, (int) $max); + $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + if ($this->overwrite) { + if (!$this->firstRun) { + // Move the cursor to the beginning of the line + $this->output->write("\x0D"); + + // Erase the line + $this->output->write("\x1B[2K"); + + // Erase previous lines + if ($this->formatLineCount > 0) { + $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount)); + } + } + } elseif ($this->step > 0) { + $this->output->writeln(''); + } + + $this->firstRun = false; + + $this->output->write($message); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->max ? 'verbose' : 'verbose_nomax'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + return $this->max ? 'very_verbose' : 'very_verbose_nomax'; + case OutputInterface::VERBOSITY_DEBUG: + return $this->max ? 'debug' : 'debug_nomax'; + default: + return $this->max ? 'normal' : 'normal_nomax'; + } + } + + private static function initPlaceholderFormatters() + { + return array( + 'bar' => function (ProgressBar $bar, OutputInterface $output) { + $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); + $display = str_repeat($bar->getBarCharacter(), $completeBars); + if ($completeBars < $bar->getBarWidth()) { + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); + $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); + } + + return $display; + }, + 'elapsed' => function (ProgressBar $bar) { + return Helper::formatTime(time() - $bar->getStartTime()); + }, + 'remaining' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $remaining = 0; + } else { + $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); + } + + return Helper::formatTime($remaining); + }, + 'estimated' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $estimated = 0; + } else { + $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); + } + + return Helper::formatTime($estimated); + }, + 'memory' => function (ProgressBar $bar) { + return Helper::formatMemory(memory_get_usage(true)); + }, + 'current' => function (ProgressBar $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); + }, + 'max' => function (ProgressBar $bar) { + return $bar->getMaxSteps(); + }, + 'percent' => function (ProgressBar $bar) { + return floor($bar->getProgressPercent() * 100); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', + 'normal_nomax' => ' %current% [%bar%]', + + 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ); + } +} diff --git a/vendor/symfony/console/Helper/ProgressIndicator.php b/vendor/symfony/console/Helper/ProgressIndicator.php new file mode 100644 index 00000000000..f90a85c2918 --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressIndicator.php @@ -0,0 +1,288 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Kevin Bond + */ +class ProgressIndicator +{ + private $output; + private $startTime; + private $format; + private $message; + private $indicatorValues; + private $indicatorCurrent; + private $indicatorChangeInterval; + private $indicatorUpdateTime; + private $lastMessagesLength; + private $started = false; + + private static $formatters; + private static $formats; + + /** + * @param OutputInterface $output + * @param string|null $format Indicator format + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters + */ + public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + { + $this->output = $output; + + if (null === $format) { + $format = $this->determineBestFormat(); + } + + if (null === $indicatorValues) { + $indicatorValues = array('-', '\\', '|', '/'); + } + + $indicatorValues = array_values($indicatorValues); + + if (2 > count($indicatorValues)) { + throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); + } + + $this->format = self::getFormatDefinition($format); + $this->indicatorChangeInterval = $indicatorChangeInterval; + $this->indicatorValues = $indicatorValues; + $this->startTime = time(); + } + + /** + * Sets the current indicator message. + * + * @param string|null $message + */ + public function setMessage($message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Starts the indicator output. + * + * @param $message + */ + public function start($message) + { + if ($this->started) { + throw new LogicException('Progress indicator already started.'); + } + + $this->message = $message; + $this->started = true; + $this->lastMessagesLength = 0; + $this->startTime = time(); + $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; + $this->indicatorCurrent = 0; + + $this->display(); + } + + /** + * Advances the indicator. + */ + public function advance() + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + if (!$this->output->isDecorated()) { + return; + } + + $currentTime = $this->getCurrentTimeInMilliseconds(); + + if ($currentTime < $this->indicatorUpdateTime) { + return; + } + + $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; + ++$this->indicatorCurrent; + + $this->display(); + } + + /** + * Finish the indicator with message. + * + * @param $message + */ + public function finish($message) + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + $this->message = $message; + $this->display(); + $this->output->writeln(''); + $this->started = false; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + private function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $self = $this; + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + return call_user_func($formatter, $self); + } + + return $matches[0]; + }, $this->format)); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; + default: + return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; + } + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + // append whitespace to match the line's length + if (null !== $this->lastMessagesLength) { + if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $message)) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + } + + if ($this->output->isDecorated()) { + $this->output->write("\x0D"); + $this->output->write($message); + } else { + $this->output->writeln($message); + } + + $this->lastMessagesLength = 0; + + $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $message); + + if ($len > $this->lastMessagesLength) { + $this->lastMessagesLength = $len; + } + } + + private function getCurrentTimeInMilliseconds() + { + return round(microtime(true) * 1000); + } + + private static function initPlaceholderFormatters() + { + return array( + 'indicator' => function (ProgressIndicator $indicator) { + return $indicator->indicatorValues[$indicator->indicatorCurrent % count($indicator->indicatorValues)]; + }, + 'message' => function (ProgressIndicator $indicator) { + return $indicator->message; + }, + 'elapsed' => function (ProgressIndicator $indicator) { + return Helper::formatTime(time() - $indicator->startTime); + }, + 'memory' => function () { + return Helper::formatMemory(memory_get_usage(true)); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ); + } +} diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php new file mode 100644 index 00000000000..c6f5a409c3d --- /dev/null +++ b/vendor/symfony/console/Helper/QuestionHelper.php @@ -0,0 +1,445 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ChoiceQuestion; + +/** + * The QuestionHelper class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class QuestionHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks a question to the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * @param Question $question The question to ask + * + * @return string The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if (!$input->isInteractive()) { + return $question->getDefault(); + } + + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } + + $interviewer = function () use ($output, $question) { + return $this->doAsk($output, $question); + }; + + return $this->validateAttempts($interviewer, $output, $question); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + * + * @throws InvalidArgumentException In case the stream is not a resource + */ + public function setInputStream($stream) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Input stream must be a valid resource.'); + } + + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @return resource + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'question'; + } + + /** + * Asks the question to the user. + * + * @param OutputInterface $output + * @param Question $question + * + * @return bool|mixed|null|string + * + * @throws \Exception + * @throws \RuntimeException + */ + private function doAsk(OutputInterface $output, Question $question) + { + $this->writePrompt($output, $question); + + $inputStream = $this->inputStream ?: STDIN; + $autocomplete = $question->getAutocompleterValues(); + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = false; + if ($question->isHidden()) { + try { + $ret = trim($this->getHiddenResponse($output, $inputStream)); + } catch (\RuntimeException $e) { + if (!$question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + } + } else { + $ret = trim($this->autocomplete($output, $question, $inputStream)); + } + + $ret = strlen($ret) > 0 ? $ret : $question->getDefault(); + + if ($normalizer = $question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + /** + * Outputs the question prompt. + * + * @param OutputInterface $output + * @param Question $question + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $message = $question->getQuestion(); + + if ($question instanceof ChoiceQuestion) { + $maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices()))); + + $messages = (array) $question->getQuestion(); + foreach ($question->getChoices() as $key => $value) { + $width = $maxWidth - $this->strlen($key); + $messages[] = ' ['.$key.str_repeat(' ', $width).'] '.$value; + } + + $output->writeln($messages); + + $message = $question->getPrompt(); + } + + $output->write($message); + } + + /** + * Outputs an error message. + * + * @param OutputInterface $output + * @param \Exception $error + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { + $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); + } else { + $message = ''.$error->getMessage().''; + } + + $output->writeln($message); + } + + /** + * Autocompletes a question. + * + * @param OutputInterface $output + * @param Question $question + * + * @return string + */ + private function autocomplete(OutputInterface $output, Question $question, $inputStream) + { + $autocomplete = $question->getAutocompleterValues(); + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write(''.substr($matches[$ofs], $i).''); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + + return $ret; + } + + /** + * Gets a hidden response from user. + * + * @param OutputInterface $output An Output instance + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function getHiddenResponse(OutputInterface $output, $inputStream) + { + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($inputStream, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + throw new RuntimeException('Unable to hide the response.'); + } + + /** + * Validates an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param Question $question A Question instance + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question) + { + $error = null; + $attempts = $question->getMaxAttempts(); + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->writeError($output, $error); + } + + try { + return call_user_func($question->getValidator(), $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Returns a valid unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + /** + * Returns whether Stty is available or not. + * + * @return bool + */ + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Helper/SymfonyQuestionHelper.php b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php new file mode 100644 index 00000000000..b17482556ee --- /dev/null +++ b/vendor/symfony/console/Helper/SymfonyQuestionHelper.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 Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Symfony Style Guide compliant question helper. + * + * @author Kevin Bond + */ +class SymfonyQuestionHelper extends QuestionHelper +{ + /** + * {@inheritdoc} + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + $validator = $question->getValidator(); + $question->setValidator(function ($value) use ($validator) { + if (null !== $validator) { + $value = $validator($value); + } + + // make required + if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { + throw new LogicException('A value is required.'); + } + + return $value; + }); + + return parent::ask($input, $output, $question); + } + + /** + * {@inheritdoc} + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $text = $question->getQuestion(); + $default = $question->getDefault(); + + switch (true) { + case null === $default: + $text = sprintf(' %s:', $text); + + break; + + case $question instanceof ConfirmationQuestion: + $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + + break; + + case $question instanceof ChoiceQuestion && $question->isMultiSelect(): + $choices = $question->getChoices(); + $default = explode(',', $default); + + foreach ($default as $key => $value) { + $default[$key] = $choices[trim($value)]; + } + + $text = sprintf(' %s [%s]:', $text, implode(', ', $default)); + + break; + + case $question instanceof ChoiceQuestion: + $choices = $question->getChoices(); + $text = sprintf(' %s [%s]:', $text, $choices[$default]); + + break; + + default: + $text = sprintf(' %s [%s]:', $text, $default); + } + + $output->writeln($text); + + if ($question instanceof ChoiceQuestion) { + $width = max(array_map('strlen', array_keys($question->getChoices()))); + + foreach ($question->getChoices() as $key => $value) { + $output->writeln(sprintf(" [%-${width}s] %s", $key, $value)); + } + } + + $output->write(' > '); + } + + /** + * {@inheritdoc} + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if ($output instanceof SymfonyStyle) { + $output->newLine(); + $output->error($error->getMessage()); + + return; + } + + parent::writeError($output, $error); + } +} diff --git a/vendor/symfony/console/Helper/Table.php b/vendor/symfony/console/Helper/Table.php new file mode 100644 index 00000000000..e66594021c7 --- /dev/null +++ b/vendor/symfony/console/Helper/Table.php @@ -0,0 +1,704 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Provides helpers to display a table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + * @author Abdellatif Ait boudad + * @author Max Grigorian + */ +class Table +{ + /** + * Table headers. + * + * @var array + */ + private $headers = array(); + + /** + * Table rows. + * + * @var array + */ + private $rows = array(); + + /** + * Column widths cache. + * + * @var array + */ + private $effectiveColumnWidths = array(); + + /** + * Number of columns cache. + * + * @var array + */ + private $numberOfColumns; + + /** + * @var OutputInterface + */ + private $output; + + /** + * @var TableStyle + */ + private $style; + + /** + * @var array + */ + private $columnStyles = array(); + + /** + * User set column widths. + * + * @var array + */ + private $columnWidths = array(); + + private static $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + * + * @param string $name The style name + * @param TableStyle $style A TableStyle instance + */ + public static function setStyleDefinition($name, TableStyle $style) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + self::$styles[$name] = $style; + } + + /** + * Gets a style definition by name. + * + * @param string $name The style name + * + * @return TableStyle A TableStyle instance + */ + public static function getStyleDefinition($name) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + if (!self::$styles[$name]) { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return self::$styles[$name]; + } + + /** + * Sets table style. + * + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setStyle($name) + { + if ($name instanceof TableStyle) { + $this->style = $name; + } elseif (isset(self::$styles[$name])) { + $this->style = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current table style. + * + * @return TableStyle + */ + public function getStyle() + { + return $this->style; + } + + /** + * Sets table column style. + * + * @param int $columnIndex Column index + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setColumnStyle($columnIndex, $name) + { + $columnIndex = intval($columnIndex); + + if ($name instanceof TableStyle) { + $this->columnStyles[$columnIndex] = $name; + } elseif (isset(self::$styles[$name])) { + $this->columnStyles[$columnIndex] = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current style for a column. + * + * If style was not set, it returns the global table style. + * + * @param int $columnIndex Column index + * + * @return TableStyle + */ + public function getColumnStyle($columnIndex) + { + if (isset($this->columnStyles[$columnIndex])) { + return $this->columnStyles[$columnIndex]; + } + + return $this->getStyle(); + } + + /** + * Sets the minimum width of a column. + * + * @param int $columnIndex Column index + * @param int $width Minimum column width in characters + * + * @return Table + */ + public function setColumnWidth($columnIndex, $width) + { + $this->columnWidths[intval($columnIndex)] = intval($width); + + return $this; + } + + /** + * Sets the minimum width of all columns. + * + * @param array $widths + * + * @return Table + */ + public function setColumnWidths(array $widths) + { + $this->columnWidths = array(); + foreach ($widths as $index => $width) { + $this->setColumnWidth($index, $width); + } + + return $this; + } + + public function setHeaders(array $headers) + { + $headers = array_values($headers); + if (!empty($headers) && !is_array($headers[0])) { + $headers = array($headers); + } + + $this->headers = $headers; + + return $this; + } + + public function setRows(array $rows) + { + $this->rows = array(); + + return $this->addRows($rows); + } + + public function addRows(array $rows) + { + foreach ($rows as $row) { + $this->addRow($row); + } + + return $this; + } + + public function addRow($row) + { + if ($row instanceof TableSeparator) { + $this->rows[] = $row; + + return $this; + } + + if (!is_array($row)) { + throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); + } + + $this->rows[] = array_values($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + */ + public function render() + { + $this->calculateNumberOfColumns(); + $rows = $this->buildTableRows($this->rows); + $headers = $this->buildTableRows($this->headers); + + $this->calculateColumnsWidth(array_merge($headers, $rows)); + + $this->renderRowSeparator(); + if (!empty($headers)) { + foreach ($headers as $header) { + $this->renderRow($header, $this->style->getCellHeaderFormat()); + $this->renderRowSeparator(); + } + } + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + $this->renderRowSeparator(); + } else { + $this->renderRow($row, $this->style->getCellRowFormat()); + } + } + if (!empty($rows)) { + $this->renderRowSeparator(); + } + + $this->cleanup(); + } + + /** + * Renders horizontal header separator. + * + * Example: +-----+-----------+-------+ + */ + private function renderRowSeparator() + { + if (0 === $count = $this->numberOfColumns) { + return; + } + + if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + return; + } + + $markup = $this->style->getCrossingChar(); + for ($column = 0; $column < $count; ++$column) { + $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->effectiveColumnWidths[$column]).$this->style->getCrossingChar(); + } + + $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator() + { + return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); + } + + /** + * Renders table row. + * + * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * + * @param array $row + * @param string $cellFormat + */ + private function renderRow(array $row, $cellFormat) + { + if (empty($row)) { + return; + } + + $rowContent = $this->renderColumnSeparator(); + foreach ($this->getRowColumns($row) as $column) { + $rowContent .= $this->renderCell($row, $column, $cellFormat); + $rowContent .= $this->renderColumnSeparator(); + } + $this->output->writeln($rowContent); + } + + /** + * Renders table cell with padding. + * + * @param array $row + * @param int $column + * @param string $cellFormat + */ + private function renderCell(array $row, $column, $cellFormat) + { + $cell = isset($row[$column]) ? $row[$column] : ''; + $width = $this->effectiveColumnWidths[$column]; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // add the width of the following columns(numbers of colspan). + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { + $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn]; + } + } + + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding($cell, null, true)) { + $width += strlen($cell) - mb_strwidth($cell, $encoding); + } + + $style = $this->getColumnStyle($column); + + if ($cell instanceof TableSeparator) { + return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); + } + + $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $content = sprintf($style->getCellRowContentFormat(), $cell); + + return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); + } + + /** + * Calculate number of columns for this table. + */ + private function calculateNumberOfColumns() + { + if (null !== $this->numberOfColumns) { + return; + } + + $columns = array(0); + foreach (array_merge($this->headers, $this->rows) as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $columns[] = $this->getNumberOfColumns($row); + } + + $this->numberOfColumns = max($columns); + } + + private function buildTableRows($rows) + { + $unmergedRows = array(); + for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) { + $rows = $this->fillNextRows($rows, $rowKey); + + // Remove any new line breaks and replace it with a new line + foreach ($rows[$rowKey] as $column => $cell) { + if (!strstr($cell, "\n")) { + continue; + } + $lines = explode("\n", $cell); + foreach ($lines as $lineKey => $line) { + if ($cell instanceof TableCell) { + $line = new TableCell($line, array('colspan' => $cell->getColspan())); + } + if (0 === $lineKey) { + $rows[$rowKey][$column] = $line; + } else { + $unmergedRows[$rowKey][$lineKey][$column] = $line; + } + } + } + } + + $tableRows = array(); + foreach ($rows as $rowKey => $row) { + $tableRows[] = $this->fillCells($row); + if (isset($unmergedRows[$rowKey])) { + $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); + } + } + + return $tableRows; + } + + /** + * fill rows that contains rowspan > 1. + * + * @param array $rows + * @param int $line + * + * @return array + */ + private function fillNextRows($rows, $line) + { + $unmergedRows = array(); + foreach ($rows[$line] as $column => $cell) { + if ($cell instanceof TableCell && $cell->getRowspan() > 1) { + $nbLines = $cell->getRowspan() - 1; + $lines = array($cell); + if (strstr($cell, "\n")) { + $lines = explode("\n", $cell); + $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + + $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); + unset($lines[0]); + } + + // create a two dimensional array (rowspan x colspan) + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, ''), $unmergedRows); + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); + } + } + } + + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + // we need to know if $unmergedRow will be merged or inserted into $rows + if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + foreach ($unmergedRow as $cellKey => $cell) { + // insert cell into row at cellKey position + array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); + } + } else { + $row = $this->copyRow($rows, $unmergedRowKey - 1); + foreach ($unmergedRow as $column => $cell) { + if (!empty($cell)) { + $row[$column] = $unmergedRow[$column]; + } + } + array_splice($rows, $unmergedRowKey, 0, array($row)); + } + } + + return $rows; + } + + /** + * fill cells for a row that contains colspan > 1. + * + * @param array $row + * + * @return array + */ + private function fillCells($row) + { + $newRow = array(); + foreach ($row as $column => $cell) { + $newRow[] = $cell; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { + // insert empty value at column position + $newRow[] = ''; + } + } + } + + return $newRow ?: $row; + } + + /** + * @param array $rows + * @param int $line + * + * @return array + */ + private function copyRow($rows, $line) + { + $row = $rows[$line]; + foreach ($row as $cellKey => $cellValue) { + $row[$cellKey] = ''; + if ($cellValue instanceof TableCell) { + $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); + } + } + + return $row; + } + + /** + * Gets number of columns by row. + * + * @param array $row + * + * @return int + */ + private function getNumberOfColumns(array $row) + { + $columns = count($row); + foreach ($row as $column) { + $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; + } + + return $columns; + } + + /** + * Gets list of columns for the given row. + * + * @param array $row + * + * @return array + */ + private function getRowColumns($row) + { + $columns = range(0, $this->numberOfColumns - 1); + foreach ($row as $cellKey => $cell) { + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // exclude grouped columns. + $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); + } + } + + return $columns; + } + + /** + * Calculates columns widths. + * + * @param array $rows + */ + private function calculateColumnsWidth($rows) + { + for ($column = 0; $column < $this->numberOfColumns; ++$column) { + $lengths = array(); + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + foreach ($row as $i => $cell) { + if ($cell instanceof TableCell) { + $textLength = strlen($cell); + if ($textLength > 0) { + $contentColumns = str_split($cell, ceil($textLength / $cell->getColspan())); + foreach ($contentColumns as $position => $content) { + $row[$i + $position] = $content; + } + } + } + } + + $lengths[] = $this->getCellWidth($row, $column); + } + + $this->effectiveColumnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; + } + } + + /** + * Gets column width. + * + * @return int + */ + private function getColumnSeparatorWidth() + { + return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Gets cell width. + * + * @param array $row + * @param int $column + * + * @return int + */ + private function getCellWidth(array $row, $column) + { + $cellWidth = 0; + + if (isset($row[$column])) { + $cell = $row[$column]; + $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + } + + $columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0; + + return max($cellWidth, $columnWidth); + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->effectiveColumnWidths = array(); + $this->numberOfColumns = null; + } + + private static function initStyles() + { + $borderless = new TableStyle(); + $borderless + ->setHorizontalBorderChar('=') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ; + + $compact = new TableStyle(); + $compact + ->setHorizontalBorderChar('') + ->setVerticalBorderChar(' ') + ->setCrossingChar('') + ->setCellRowContentFormat('%s') + ; + + $styleGuide = new TableStyle(); + $styleGuide + ->setHorizontalBorderChar('-') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ->setCellHeaderFormat('%s') + ; + + return array( + 'default' => new TableStyle(), + 'borderless' => $borderless, + 'compact' => $compact, + 'symfony-style-guide' => $styleGuide, + ); + } +} diff --git a/vendor/symfony/console/Helper/TableCell.php b/vendor/symfony/console/Helper/TableCell.php new file mode 100644 index 00000000000..69442d4249c --- /dev/null +++ b/vendor/symfony/console/Helper/TableCell.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class TableCell +{ + /** + * @var string + */ + private $value; + + /** + * @var array + */ + private $options = array( + 'rowspan' => 1, + 'colspan' => 1, + ); + + /** + * @param string $value + * @param array $options + */ + public function __construct($value = '', array $options = array()) + { + $this->value = $value; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + } + + /** + * Returns the cell value. + * + * @return string + */ + public function __toString() + { + return $this->value; + } + + /** + * Gets number of colspan. + * + * @return int + */ + public function getColspan() + { + return (int) $this->options['colspan']; + } + + /** + * Gets number of rowspan. + * + * @return int + */ + public function getRowspan() + { + return (int) $this->options['rowspan']; + } +} diff --git a/vendor/symfony/console/Helper/TableSeparator.php b/vendor/symfony/console/Helper/TableSeparator.php new file mode 100644 index 00000000000..8cc73e69a25 --- /dev/null +++ b/vendor/symfony/console/Helper/TableSeparator.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 Symfony\Component\Console\Helper; + +/** + * Marks a row as being a separator. + * + * @author Fabien Potencier + */ +class TableSeparator extends TableCell +{ + /** + * @param array $options + */ + public function __construct(array $options = array()) + { + parent::__construct('', $options); + } +} diff --git a/vendor/symfony/console/Helper/TableStyle.php b/vendor/symfony/console/Helper/TableStyle.php new file mode 100644 index 00000000000..d7e28ff2b4e --- /dev/null +++ b/vendor/symfony/console/Helper/TableStyle.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Defines the styles for a Table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + */ +class TableStyle +{ + private $paddingChar = ' '; + private $horizontalBorderChar = '-'; + private $verticalBorderChar = '|'; + private $crossingChar = '+'; + private $cellHeaderFormat = '%s'; + private $cellRowFormat = '%s'; + private $cellRowContentFormat = ' %s '; + private $borderFormat = '%s'; + private $padType = STR_PAD_RIGHT; + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableStyle + */ + public function setPaddingChar($paddingChar) + { + if (!$paddingChar) { + throw new LogicException('The padding char must not be empty'); + } + + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Gets padding character, used for cell padding. + * + * @return string + */ + public function getPaddingChar() + { + return $this->paddingChar; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableStyle + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->horizontalBorderChar = $horizontalBorderChar; + + return $this; + } + + /** + * Gets horizontal border character. + * + * @return string + */ + public function getHorizontalBorderChar() + { + return $this->horizontalBorderChar; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableStyle + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->verticalBorderChar = $verticalBorderChar; + + return $this; + } + + /** + * Gets vertical border character. + * + * @return string + */ + public function getVerticalBorderChar() + { + return $this->verticalBorderChar; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableStyle + */ + public function setCrossingChar($crossingChar) + { + $this->crossingChar = $crossingChar; + + return $this; + } + + /** + * Gets crossing character. + * + * @return string $crossingChar + */ + public function getCrossingChar() + { + return $this->crossingChar; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableStyle + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Gets header cell format. + * + * @return string + */ + public function getCellHeaderFormat() + { + return $this->cellHeaderFormat; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableStyle + */ + public function setCellRowFormat($cellRowFormat) + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Gets row cell format. + * + * @return string + */ + public function getCellRowFormat() + { + return $this->cellRowFormat; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return TableStyle + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->cellRowContentFormat = $cellRowContentFormat; + + return $this; + } + + /** + * Gets row cell content format. + * + * @return string + */ + public function getCellRowContentFormat() + { + return $this->cellRowContentFormat; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableStyle + */ + public function setBorderFormat($borderFormat) + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Gets table border format. + * + * @return string + */ + public function getBorderFormat() + { + return $this->borderFormat; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return TableStyle + */ + public function setPadType($padType) + { + if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) { + throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); + } + + $this->padType = $padType; + + return $this; + } + + /** + * Gets cell padding type. + * + * @return int + */ + public function getPadType() + { + return $this->padType; + } +} diff --git a/vendor/symfony/console/Input/ArgvInput.php b/vendor/symfony/console/Input/ArgvInput.php new file mode 100644 index 00000000000..4a0fa91bfa4 --- /dev/null +++ b/vendor/symfony/console/Input/ArgvInput.php @@ -0,0 +1,339 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array|null $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition|null $definition A InputDefinition instance + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * {@inheritdoc} + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), null); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + // Convert empty values to null + if (!isset($value[0])) { + $value = null; + } + + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + + if (null === $value && $option->acceptValue() && count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if (isset($next[0]) && '-' !== $next[0]) { + $value = $next; + } elseif (empty($next)) { + $value = ''; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isArray()) { + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * {@inheritdoc} + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * {@inheritdoc} + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + if ($onlyParams && $token === '--') { + return false; + } + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + return true; + } + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < count($tokens)) { + $token = array_shift($tokens); + if ($onlyParams && $token === '--') { + return false; + } + + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $tokens = array_map(function ($token) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1].$this->escapeToken($match[2]); + } + + if ($token && $token[0] !== '-') { + return $this->escapeToken($token); + } + + return $token; + }, $this->tokens); + + return implode(' ', $tokens); + } +} diff --git a/vendor/symfony/console/Input/ArrayInput.php b/vendor/symfony/console/Input/ArrayInput.php new file mode 100644 index 00000000000..a44b6b2815c --- /dev/null +++ b/vendor/symfony/console/Input/ArrayInput.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition|null $definition A InputDefinition instance + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * {@inheritdoc} + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * {@inheritdoc} + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if ($onlyParams && $v === '--') { + return false; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if ($onlyParams && ($k === '--' || (is_int($k) && $v === '--'))) { + return false; + } + + if (is_int($k)) { + if (in_array($v, $values)) { + return true; + } + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $params = array(); + foreach ($this->parameters as $param => $val) { + if ($param && '-' === $param[0]) { + $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + } else { + $params[] = $this->escapeToken($val); + } + } + + return implode(' ', $params); + } + + /** + * {@inheritdoc} + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if ($key === '--') { + return; + } + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + * @throws InvalidOptionException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/vendor/symfony/console/Input/Input.php b/vendor/symfony/console/Input/Input.php new file mode 100644 index 00000000000..817292ed730 --- /dev/null +++ b/vendor/symfony/console/Input/Input.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface +{ + /** + * @var InputDefinition + */ + protected $definition; + protected $options = array(); + protected $arguments = array(); + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition|null $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * {@inheritdoc} + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * {@inheritdoc} + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { + return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + }); + + if (count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + /** + * {@inheritdoc} + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * {@inheritdoc} + */ + public function setInteractive($interactive) + { + $this->interactive = (bool) $interactive; + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * {@inheritdoc} + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * {@inheritdoc} + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * {@inheritdoc} + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * {@inheritdoc} + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } + + /** + * Escapes a token through escapeshellarg if it contains unsafe chars. + * + * @param string $token + * + * @return string + */ + public function escapeToken($token) + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } +} diff --git a/vendor/symfony/console/Input/InputArgument.php b/vendor/symfony/console/Input/InputArgument.php new file mode 100644 index 00000000000..048ee4ff6b5 --- /dev/null +++ b/vendor/symfony/console/Input/InputArgument.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return bool true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/vendor/symfony/console/Input/InputAwareInterface.php b/vendor/symfony/console/Input/InputAwareInterface.php new file mode 100644 index 00000000000..d0f11e986a3 --- /dev/null +++ b/vendor/symfony/console/Input/InputAwareInterface.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 Symfony\Component\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + * + * @param InputInterface + */ + public function setInput(InputInterface $input); +} diff --git a/vendor/symfony/console/Input/InputDefinition.php b/vendor/symfony/console/Input/InputDefinition.php new file mode 100644 index 00000000000..e9944db984b --- /dev/null +++ b/vendor/symfony/console/Input/InputDefinition.php @@ -0,0 +1,411 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|int $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] An array of InputArgument objects + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return int The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return int The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } + + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) { + throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + } + } + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + $this->shortcuts[$shortcut] = $option->getName(); + } + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @param bool $short Whether to return the short version (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $elements = array(); + + if ($short && $this->getOptions()) { + $elements[] = '[options]'; + } elseif (!$short) { + foreach ($this->getOptions() as $option) { + $value = ''; + if ($option->acceptValue()) { + $value = sprintf( + ' %s%s%s', + $option->isValueOptional() ? '[' : '', + strtoupper($option->getName()), + $option->isValueOptional() ? ']' : '' + ); + } + + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + } + } + + if (count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + foreach ($this->getArguments() as $argument) { + $element = '<'.$argument->getName().'>'; + if (!$argument->isRequired()) { + $element = '['.$element.']'; + } elseif ($argument->isArray()) { + $element = $element.' ('.$element.')'; + } + + if ($argument->isArray()) { + $element .= '...'; + } + + $elements[] = $element; + } + + return implode(' ', $elements); + } +} diff --git a/vendor/symfony/console/Input/InputInterface.php b/vendor/symfony/console/Input/InputInterface.php new file mode 100644 index 00000000000..bc66466437f --- /dev/null +++ b/vendor/symfony/console/Input/InputInterface.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition); + + /** + * Validates the input. + * + * @throws RuntimeException When not enough arguments are given + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(); + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(); + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return bool + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive); +} diff --git a/vendor/symfony/console/Input/InputOption.php b/vendor/symfony/console/Input/InputOption.php new file mode 100644 index 00000000000..f08c5f26c10 --- /dev/null +++ b/vendor/symfony/console/Input/InputOption.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if (is_array($shortcut)) { + $shortcut = implode('|', $shortcut); + } + $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); + $shortcuts = array_filter($shortcuts); + $shortcut = implode('|', $shortcuts); + + if (empty($shortcut)) { + throw new InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return bool true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one. + * + * @param InputOption $option option to compare + * + * @return bool + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/vendor/symfony/console/Input/StringInput.php b/vendor/symfony/console/Input/StringInput.php new file mode 100644 index 00000000000..9ce021745f2 --- /dev/null +++ b/vendor/symfony/console/Input/StringInput.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 Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @return array An array of tokens + * + * @throws InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/vendor/symfony/console/LICENSE b/vendor/symfony/console/LICENSE new file mode 100644 index 00000000000..12a74531e40 --- /dev/null +++ b/vendor/symfony/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 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/symfony/console/Logger/ConsoleLogger.php b/vendor/symfony/console/Logger/ConsoleLogger.php new file mode 100644 index 00000000000..1f7417ea5aa --- /dev/null +++ b/vendor/symfony/console/Logger/ConsoleLogger.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 Symfony\Component\Console\Logger; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * PSR-3 compliant console logger. + * + * @author Kévin Dunglas + * + * @link http://www.php-fig.org/psr/psr-3/ + */ +class ConsoleLogger extends AbstractLogger +{ + const INFO = 'info'; + const ERROR = 'error'; + + /** + * @var OutputInterface + */ + private $output; + /** + * @var array + */ + private $verbosityLevelMap = array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, + ); + /** + * @var array + */ + private $formatLevelMap = array( + LogLevel::EMERGENCY => self::ERROR, + LogLevel::ALERT => self::ERROR, + LogLevel::CRITICAL => self::ERROR, + LogLevel::ERROR => self::ERROR, + LogLevel::WARNING => self::INFO, + LogLevel::NOTICE => self::INFO, + LogLevel::INFO => self::INFO, + LogLevel::DEBUG => self::INFO, + ); + + /** + * @param OutputInterface $output + * @param array $verbosityLevelMap + * @param array $formatLevelMap + */ + public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array()) + { + $this->output = $output; + $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; + $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) + { + if (!isset($this->verbosityLevelMap[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + // Write to the error output if necessary and available + if ($this->formatLevelMap[$level] === self::ERROR && $this->output instanceof ConsoleOutputInterface) { + $output = $this->output->getErrorOutput(); + } else { + $output = $this->output; + } + + if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { + $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context))); + } + } + + /** + * Interpolates context values into the message placeholders. + * + * @author PHP Framework Interoperability Group + * + * @param string $message + * @param array $context + * + * @return string + */ + private function interpolate($message, array $context) + { + // build a replacement array with braces around the context keys + $replace = array(); + foreach ($context as $key => $val) { + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace[sprintf('{%s}', $key)] = $val; + } + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } +} diff --git a/vendor/symfony/console/Output/BufferedOutput.php b/vendor/symfony/console/Output/BufferedOutput.php new file mode 100644 index 00000000000..5682fc2404f --- /dev/null +++ b/vendor/symfony/console/Output/BufferedOutput.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * @author Jean-François Simon + */ +class BufferedOutput extends Output +{ + /** + * @var string + */ + private $buffer = ''; + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= "\n"; + } + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutput.php b/vendor/symfony/console/Output/ConsoleOutput.php new file mode 100644 index 00000000000..007f3f01be3 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutput.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR. + * + * This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * $stdErr = new StreamOutput(fopen('php://stderr', 'w')); + * + * @author Fabien Potencier + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + /** + * @var StreamOutput + */ + private $stderr; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + + $actualDecorated = $this->isDecorated(); + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); + + if (null === $decorated) { + $this->setDecorated($actualDecorated && $this->stderr->isDecorated()); + } + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getErrorOutput() + { + return $this->stderr; + } + + /** + * {@inheritdoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + * + * @return bool + */ + protected function hasStdoutSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Returns true if current environment supports writing console output to + * STDERR. + * + * @return bool + */ + protected function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Checks if current executing environment is IBM iSeries (OS400), which + * doesn't properly convert character-encodings between ASCII to EBCDIC. + * + * @return bool + */ + private function isRunningOS400() + { + $checks = array( + function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + PHP_OS, + ); + + return false !== stripos(implode(';', $checks), 'OS400'); + } + + /** + * @return resource + */ + private function openOutputStream() + { + if (!$this->hasStdoutSupport()) { + return fopen('php://output', 'w'); + } + + return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w'); + } + + /** + * @return resource + */ + private function openErrorStream() + { + return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w'); + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutputInterface.php b/vendor/symfony/console/Output/ConsoleOutputInterface.php new file mode 100644 index 00000000000..5eb4fc7acde --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutputInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * Gets the OutputInterface for errors. + * + * @return OutputInterface + */ + public function getErrorOutput(); + + /** + * Sets the OutputInterface used for errors. + * + * @param OutputInterface $error + */ + public function setErrorOutput(OutputInterface $error); +} diff --git a/vendor/symfony/console/Output/NullOutput.php b/vendor/symfony/console/Output/NullOutput.php new file mode 100644 index 00000000000..218f285bfe5 --- /dev/null +++ b/vendor/symfony/console/Output/NullOutput.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class NullOutput implements OutputInterface +{ + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + // to comply with the interface we must return a OutputFormatterInterface + return new OutputFormatter(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return self::VERBOSITY_QUIET; + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + // do nothing + } +} diff --git a/vendor/symfony/console/Output/Output.php b/vendor/symfony/console/Output/Output.php new file mode 100644 index 00000000000..c12015cc8fe --- /dev/null +++ b/vendor/symfony/console/Output/Output.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are five levels of verbosity: + * + * * normal: no option passed (normal output) + * * verbose: -v (more output) + * * very verbose: -vv (highly extended output) + * * debug: -vvv (all debug output) + * * quiet: -q (no output) + * + * @author Fabien Potencier + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = $formatter ?: new OutputFormatter(); + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; + $type = $types & $options ?: self::OUTPUT_NORMAL; + + $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG; + $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL; + + if ($verbosity > $this->getVerbosity()) { + return; + } + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param bool $newline Whether to add a newline or not + */ + abstract protected function doWrite($message, $newline); +} diff --git a/vendor/symfony/console/Output/OutputInterface.php b/vendor/symfony/console/Output/OutputInterface.php new file mode 100644 index 00000000000..a291ca7d7e2 --- /dev/null +++ b/vendor/symfony/console/Output/OutputInterface.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 Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 16; + const VERBOSITY_NORMAL = 32; + const VERBOSITY_VERBOSE = 64; + const VERBOSITY_VERY_VERBOSE = 128; + const VERBOSITY_DEBUG = 256; + + const OUTPUT_NORMAL = 1; + const OUTPUT_RAW = 2; + const OUTPUT_PLAIN = 4; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function write($messages, $newline = false, $options = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function writeln($messages, $options = 0); + + /** + * Sets the verbosity of the output. + * + * @param int $level The level of verbosity (one of the VERBOSITY constants) + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return int The current level of verbosity (one of the VERBOSITY constants) + */ + public function getVerbosity(); + + /** + * Returns whether verbosity is quiet (-q). + * + * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise + */ + public function isQuiet(); + + /** + * Returns whether verbosity is verbose (-v). + * + * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise + */ + public function isVerbose(); + + /** + * Returns whether verbosity is very verbose (-vv). + * + * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise + */ + public function isVeryVerbose(); + + /** + * Returns whether verbosity is debug (-vvv). + * + * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise + */ + public function isDebug(); + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + */ + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + */ + public function getFormatter(); +} diff --git a/vendor/symfony/console/Output/StreamOutput.php b/vendor/symfony/console/Output/StreamOutput.php new file mode 100644 index 00000000000..9e6b74810d2 --- /dev/null +++ b/vendor/symfony/console/Output/StreamOutput.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 Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param resource $stream A stream resource + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @throws InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message) || ($newline && (false === @fwrite($this->stream, PHP_EOL)))) { + // should never happen + throw new RuntimeException('Unable to write output.'); + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - Windows before 10.0.10586 without Ansicon, ConEmu or Mintty + * - non tty consoles + * + * @return bool true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + if (DIRECTORY_SEPARATOR === '\\') { + return + 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + } +} diff --git a/vendor/symfony/console/Question/ChoiceQuestion.php b/vendor/symfony/console/Question/ChoiceQuestion.php new file mode 100644 index 00000000000..39c4a852d08 --- /dev/null +++ b/vendor/symfony/console/Question/ChoiceQuestion.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Represents a choice question. + * + * @author Fabien Potencier + */ +class ChoiceQuestion extends Question +{ + private $choices; + private $multiselect = false; + private $prompt = ' > '; + private $errorMessage = 'Value "%s" is invalid'; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param mixed $default The default answer to return + */ + public function __construct($question, array $choices, $default = null) + { + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * Returns available choices. + * + * @return array + */ + public function getChoices() + { + return $this->choices; + } + + /** + * Sets multiselect option. + * + * When multiselect is set to true, multiple choices can be answered. + * + * @param bool $multiselect + * + * @return ChoiceQuestion The current instance + */ + public function setMultiselect($multiselect) + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns whether the choices are multiselect. + * + * @return bool + */ + public function isMultiselect() + { + return $this->multiselect; + } + + /** + * Gets the prompt for choices. + * + * @return string + */ + public function getPrompt() + { + return $this->prompt; + } + + /** + * Sets the prompt for choices. + * + * @param string $prompt + * + * @return ChoiceQuestion The current instance + */ + public function setPrompt($prompt) + { + $this->prompt = $prompt; + + return $this; + } + + /** + * Sets the error message for invalid values. + * + * The error message has a string placeholder (%s) for the invalid value. + * + * @param string $errorMessage + * + * @return ChoiceQuestion The current instance + */ + public function setErrorMessage($errorMessage) + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns the default answer validator. + * + * @return callable + */ + private function getDefaultValidator() + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $selected); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = array($selected); + } + + $multiselectChoices = array(); + foreach ($selectedChoices as $value) { + $results = array(); + foreach ($choices as $key => $choice) { + if ($choice === $value) { + $results[] = $key; + } + } + + if (count($results) > 1) { + throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + } + + $result = array_search($value, $choices); + + if (!$isAssoc) { + if (false !== $result) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (false === $result && isset($choices[$value])) { + $result = $value; + } + + if (false === $result) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + + $multiselectChoices[] = (string) $result; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/vendor/symfony/console/Question/ConfirmationQuestion.php b/vendor/symfony/console/Question/ConfirmationQuestion.php new file mode 100644 index 00000000000..29d98879f0c --- /dev/null +++ b/vendor/symfony/console/Question/ConfirmationQuestion.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +/** + * Represents a yes/no question. + * + * @author Fabien Potencier + */ +class ConfirmationQuestion extends Question +{ + private $trueAnswerRegex; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param bool $default The default answer to return, true or false + * @param string $trueAnswerRegex A regex to match the "yes" answer + */ + public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, (bool) $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * Returns the default answer normalizer. + * + * @return callable + */ + private function getDefaultNormalizer() + { + $default = $this->getDefault(); + $regex = $this->trueAnswerRegex; + + return function ($answer) use ($default, $regex) { + if (is_bool($answer)) { + return $answer; + } + + $answerIsTrue = (bool) preg_match($regex, $answer); + if (false === $default) { + return $answer && $answerIsTrue; + } + + return !$answer || $answerIsTrue; + }; + } +} diff --git a/vendor/symfony/console/Question/Question.php b/vendor/symfony/console/Question/Question.php new file mode 100644 index 00000000000..7a69279f4d4 --- /dev/null +++ b/vendor/symfony/console/Question/Question.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a Question. + * + * @author Fabien Potencier + */ +class Question +{ + private $question; + private $attempts; + private $hidden = false; + private $hiddenFallback = true; + private $autocompleterValues; + private $validator; + private $default; + private $normalizer; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param mixed $default The default answer to return if the user enters nothing + */ + public function __construct($question, $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * Returns the question. + * + * @return string + */ + public function getQuestion() + { + return $this->question; + } + + /** + * Returns the default answer. + * + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns whether the user response must be hidden. + * + * @return bool + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * Sets whether the user response must be hidden or not. + * + * @param bool $hidden + * + * @return Question The current instance + * + * @throws LogicException In case the autocompleter is also used + */ + public function setHidden($hidden) + { + if ($this->autocompleterValues) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * In case the response can not be hidden, whether to fallback on non-hidden question or not. + * + * @return bool + */ + public function isHiddenFallback() + { + return $this->hiddenFallback; + } + + /** + * Sets whether to fallback on non-hidden question if the response can not be hidden. + * + * @param bool $fallback + * + * @return Question The current instance + */ + public function setHiddenFallback($fallback) + { + $this->hiddenFallback = (bool) $fallback; + + return $this; + } + + /** + * Gets values for the autocompleter. + * + * @return null|array|\Traversable + */ + public function getAutocompleterValues() + { + return $this->autocompleterValues; + } + + /** + * Sets values for the autocompleter. + * + * @param null|array|\Traversable $values + * + * @return Question The current instance + * + * @throws InvalidArgumentException + * @throws LogicException + */ + public function setAutocompleterValues($values) + { + if (is_array($values)) { + $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values); + } + + if (null !== $values && !is_array($values)) { + if (!$values instanceof \Traversable || !$values instanceof \Countable) { + throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); + } + } + + if ($this->hidden) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterValues = $values; + + return $this; + } + + /** + * Sets a validator for the question. + * + * @param null|callable $validator + * + * @return Question The current instance + */ + public function setValidator(callable $validator = null) + { + $this->validator = $validator; + + return $this; + } + + /** + * Gets the validator for the question. + * + * @return null|callable + */ + public function getValidator() + { + return $this->validator; + } + + /** + * Sets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @param null|int $attempts + * + * @return Question The current instance + * + * @throws InvalidArgumentException In case the number of attempts is invalid. + */ + public function setMaxAttempts($attempts) + { + if (null !== $attempts && $attempts < 1) { + throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * Gets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @return null|int + */ + public function getMaxAttempts() + { + return $this->attempts; + } + + /** + * Sets a normalizer for the response. + * + * The normalizer can be a callable (a string), a closure or a class implementing __invoke. + * + * @param callable $normalizer + * + * @return Question The current instance + */ + public function setNormalizer(callable $normalizer) + { + $this->normalizer = $normalizer; + + return $this; + } + + /** + * Gets the normalizer for the response. + * + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + * + * @return callable + */ + public function getNormalizer() + { + return $this->normalizer; + } + + protected function isAssoc($array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } +} diff --git a/vendor/symfony/console/README.md b/vendor/symfony/console/README.md new file mode 100644 index 00000000000..664a37c0ee7 --- /dev/null +++ b/vendor/symfony/console/README.md @@ -0,0 +1,20 @@ +Console Component +================= + +The Console component eases the creation of beautiful and testable command line +interfaces. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/console/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) + +Credits +------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. diff --git a/vendor/symfony/console/Resources/bin/hiddeninput.exe b/vendor/symfony/console/Resources/bin/hiddeninput.exe new file mode 100644 index 00000000000..c8cf65e8d81 Binary files /dev/null and b/vendor/symfony/console/Resources/bin/hiddeninput.exe differ diff --git a/vendor/symfony/console/Style/OutputStyle.php b/vendor/symfony/console/Style/OutputStyle.php new file mode 100644 index 00000000000..de7be1e08b3 --- /dev/null +++ b/vendor/symfony/console/Style/OutputStyle.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Decorates output to add console style guide helpers. + * + * @author Kevin Bond + */ +abstract class OutputStyle implements OutputInterface, StyleInterface +{ + private $output; + + /** + * @param OutputInterface $output + */ + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + $this->output->write(str_repeat(PHP_EOL, $count)); + } + + /** + * @param int $max + * + * @return ProgressBar + */ + public function createProgressBar($max = 0) + { + return new ProgressBar($this->output, $max); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->output->getFormatter(); + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return $this->output->isQuiet(); + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return $this->output->isVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return $this->output->isVeryVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return $this->output->isDebug(); + } +} diff --git a/vendor/symfony/console/Style/StyleInterface.php b/vendor/symfony/console/Style/StyleInterface.php new file mode 100644 index 00000000000..2448547f855 --- /dev/null +++ b/vendor/symfony/console/Style/StyleInterface.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond + */ +interface StyleInterface +{ + /** + * Formats a command title. + * + * @param string $message + */ + public function title($message); + + /** + * Formats a section title. + * + * @param string $message + */ + public function section($message); + + /** + * Formats a list. + * + * @param array $elements + */ + public function listing(array $elements); + + /** + * Formats informational text. + * + * @param string|array $message + */ + public function text($message); + + /** + * Formats a success result bar. + * + * @param string|array $message + */ + public function success($message); + + /** + * Formats an error result bar. + * + * @param string|array $message + */ + public function error($message); + + /** + * Formats an warning result bar. + * + * @param string|array $message + */ + public function warning($message); + + /** + * Formats a note admonition. + * + * @param string|array $message + */ + public function note($message); + + /** + * Formats a caution admonition. + * + * @param string|array $message + */ + public function caution($message); + + /** + * Formats a table. + * + * @param array $headers + * @param array $rows + */ + public function table(array $headers, array $rows); + + /** + * Asks a question. + * + * @param string $question + * @param string|null $default + * @param callable|null $validator + * + * @return string + */ + public function ask($question, $default = null, $validator = null); + + /** + * Asks a question with the user input hidden. + * + * @param string $question + * @param callable|null $validator + * + * @return string + */ + public function askHidden($question, $validator = null); + + /** + * Asks for confirmation. + * + * @param string $question + * @param bool $default + * + * @return bool + */ + public function confirm($question, $default = true); + + /** + * Asks a choice question. + * + * @param string $question + * @param array $choices + * @param string|int|null $default + * + * @return string + */ + public function choice($question, array $choices, $default = null); + + /** + * Add newline(s). + * + * @param int $count The number of newlines + */ + public function newLine($count = 1); + + /** + * Starts the progress output. + * + * @param int $max Maximum steps (0 if unknown) + */ + public function progressStart($max = 0); + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function progressAdvance($step = 1); + + /** + * Finishes the progress output. + */ + public function progressFinish(); +} diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php new file mode 100644 index 00000000000..59c5bbf87ed --- /dev/null +++ b/vendor/symfony/console/Style/SymfonyStyle.php @@ -0,0 +1,433 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\SymfonyQuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * Output decorator helpers for the Symfony Style Guide. + * + * @author Kevin Bond + */ +class SymfonyStyle extends OutputStyle +{ + const MAX_LINE_LENGTH = 120; + + private $input; + private $questionHelper; + private $progressBar; + private $lineLength; + private $bufferedOutput; + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. + $this->lineLength = min($this->getTerminalWidth() - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + + parent::__construct($output); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string|null $type The block type (added in [] on first line) + * @param string|null $style The style to apply to the whole block + * @param string $prefix The prefix for the block + * @param bool $padding Whether to add vertical padding + */ + public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false) + { + $messages = is_array($messages) ? array_values($messages) : array($messages); + + $this->autoPrependBlock(); + $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, true)); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function title($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function section($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(function ($element) { + return sprintf(' * %s', $element); + }, $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function text($message) + { + $this->autoPrependText(); + + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as $message) { + $this->writeln(sprintf(' %s', $message)); + } + } + + /** + * Formats a command comment. + * + * @param string|array $message + */ + public function comment($message) + { + $messages = is_array($message) ? array_values($message) : array($message); + + $this->autoPrependBlock(); + $this->writeln($this->createBlock($messages, null, null, ' // ')); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function success($message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function error($message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function warning($message) + { + $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function note($message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * {@inheritdoc} + */ + public function caution($message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * {@inheritdoc} + */ + public function table(array $headers, array $rows) + { + $style = clone Table::getStyleDefinition('symfony-style-guide'); + $style->setCellHeaderFormat('%s'); + + $table = new Table($this); + $table->setHeaders($headers); + $table->setRows($rows); + $table->setStyle($style); + + $table->render(); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function ask($question, $default = null, $validator = null) + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function askHidden($question, $validator = null) + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function confirm($question, $default = true) + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice($question, array $choices, $default = null) + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default]; + } + + return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); + } + + /** + * {@inheritdoc} + */ + public function progressStart($max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * {@inheritdoc} + */ + public function progressAdvance($step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * {@inheritdoc} + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + $this->progressBar = null; + } + + /** + * {@inheritdoc} + */ + public function createProgressBar($max = 0) + { + $progressBar = parent::createProgressBar($max); + + if ('\\' !== DIRECTORY_SEPARATOR) { + $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 + $progressBar->setProgressCharacter(''); + $progressBar->setBarCharacter('▓'); // dark shade character \u2593 + } + + return $progressBar; + } + + /** + * @param Question $question + * + * @return string + */ + public function askQuestion(Question $question) + { + if ($this->input->isInteractive()) { + $this->autoPrependBlock(); + } + + if (!$this->questionHelper) { + $this->questionHelper = new SymfonyQuestionHelper(); + } + + $answer = $this->questionHelper->ask($this->input, $this, $question); + + if ($this->input->isInteractive()) { + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + parent::writeln($messages, $type); + $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + parent::write($messages, $newline, $type); + $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + parent::newLine($count); + $this->bufferedOutput->write(str_repeat("\n", $count)); + } + + /** + * @return ProgressBar + */ + private function getProgressBar() + { + if (!$this->progressBar) { + throw new RuntimeException('The ProgressBar is not started.'); + } + + return $this->progressBar; + } + + private function getTerminalWidth() + { + $application = new Application(); + $dimensions = $application->getTerminalDimensions(); + + return $dimensions[0] ?: self::MAX_LINE_LENGTH; + } + + private function autoPrependBlock() + { + $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + + if (!isset($chars[0])) { + return $this->newLine(); //empty history, so we should start with a new line. + } + //Prepend new line for each non LF chars (This means no blank line was output before) + $this->newLine(2 - substr_count($chars, "\n")); + } + + private function autoPrependText() + { + $fetched = $this->bufferedOutput->fetch(); + //Prepend new line if last char isn't EOL: + if ("\n" !== substr($fetched, -1)) { + $this->newLine(); + } + } + + private function reduceBuffer($messages) + { + // We need to know if the two last chars are PHP_EOL + // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer + return array_map(function ($value) { + return substr($value, -4); + }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages)); + } + + private function createBlock($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = false) + { + $indentLength = 0; + $prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix); + $lines = array(); + + if (null !== $type) { + $type = sprintf('[%s] ', $type); + $indentLength = strlen($type); + $lineIndentation = str_repeat(' ', $indentLength); + } + + // wrap and add newlines for each element + foreach ($messages as $key => $message) { + if ($escape) { + $message = OutputFormatter::escape($message); + } + + $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - $prefixLength - $indentLength, PHP_EOL, true))); + + if (count($messages) > 1 && $key < count($messages) - 1) { + $lines[] = ''; + } + } + + foreach ($lines as $i => &$line) { + if (null !== $type) { + $line = 0 === $i ? $type.$line : $lineIndentation.$line; + } + + $line = $prefix.$line; + $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + + if ($style) { + $line = sprintf('<%s>%s', $style, $line); + } + } + + if ($padding && $this->isDecorated()) { + array_unshift($lines, ''); + $lines[] = ''; + } + + return $lines; + } +} diff --git a/vendor/symfony/console/Tester/ApplicationTester.php b/vendor/symfony/console/Tester/ApplicationTester.php new file mode 100644 index 00000000000..c0f8c7207f2 --- /dev/null +++ b/vendor/symfony/console/Tester/ApplicationTester.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $statusCode; + /** + * @var OutputInterface + */ + private $output; + private $captureStreamsIndependently = false; + + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return int The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; + if (!$this->captureStreamsIndependently) { + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + } else { + $this->output = new ConsoleOutput( + isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL, + isset($options['decorated']) ? $options['decorated'] : null + ); + + $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); + $errorOutput->setFormatter($this->output->getFormatter()); + $errorOutput->setVerbosity($this->output->getVerbosity()); + $errorOutput->setDecorated($this->output->isDecorated()); + + $reflectedOutput = new \ReflectionObject($this->output); + $strErrProperty = $reflectedOutput->getProperty('stderr'); + $strErrProperty->setAccessible(true); + $strErrProperty->setValue($this->output, $errorOutput); + + $reflectedParent = $reflectedOutput->getParentClass(); + $streamProperty = $reflectedParent->getProperty('stream'); + $streamProperty->setAccessible(true); + $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); + } + + return $this->statusCode = $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the output written to STDERR by the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string + */ + public function getErrorOutput($normalize = false) + { + if (!$this->captureStreamsIndependently) { + throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); + } + + rewind($this->output->getErrorOutput()->getStream()); + + $display = stream_get_contents($this->output->getErrorOutput()->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tester/CommandTester.php b/vendor/symfony/console/Tester/CommandTester.php new file mode 100644 index 00000000000..f95298bc90c --- /dev/null +++ b/vendor/symfony/console/Tester/CommandTester.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Command $command A Command instance to test + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available execution options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of command arguments and options + * @param array $options An array of execution options + * + * @return int The command exit code + */ + public function execute(array $input, array $options = array()) + { + // set the command name automatically if the application requires + // this argument and no command name was passed + if (!isset($input['command']) + && (null !== $application = $this->command->getApplication()) + && $application->getDefinition()->hasArgument('command') + ) { + $input = array_merge(array('command' => $this->command->getName()), $input); + } + + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tests/ApplicationTest.php b/vendor/symfony/console/Tests/ApplicationTest.php new file mode 100644 index 00000000000..954d02eaad9 --- /dev/null +++ b/vendor/symfony/console/Tests/ApplicationTest.php @@ -0,0 +1,1143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ApplicationTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); + require_once self::$fixturesPath.'/FooCommand.php'; + require_once self::$fixturesPath.'/Foo1Command.php'; + require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; + require_once self::$fixturesPath.'/Foo4Command.php'; + require_once self::$fixturesPath.'/Foo5Command.php'; + require_once self::$fixturesPath.'/FoobarCommand.php'; + require_once self::$fixturesPath.'/BarBucCommand.php'; + require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; + require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; + } + + protected function normalizeLineBreaks($text) + { + return str_replace(PHP_EOL, "\n", $text); + } + + /** + * Replaces the dynamic placeholders of the command help text with a static version. + * The placeholder %command.full_name% includes the script path that is not predictable + * and can not be tested against. + */ + protected function ensureStaticCommandHelp(Application $application) + { + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + } + + public function testConstructor() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); + $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument'); + $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default'); + } + + public function testSetGetName() + { + $application = new Application(); + $application->setName('foo'); + $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); + } + + public function testSetGetVersion() + { + $application = new Application(); + $application->setVersion('bar'); + $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); + } + + public function testGetLongVersion() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo version bar', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); + } + + public function testHelp() + { + $application = new Application(); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); + } + + public function testRegister() + { + $application = new Application(); + $command = $application->register('foo'); + $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); + } + + public function testAdd() + { + $application = new Application(); + $application->add($foo = new \FooCommand()); + $commands = $application->all(); + $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); + + $application = new Application(); + $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command())); + $commands = $application->all(); + $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor. + */ + public function testAddCommandWithEmptyConstructor() + { + $application = new Application(); + $application->add(new \Foo5Command()); + } + + public function testHasGet() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + $application = new Application(); + $application->add($foo = new \FooCommand()); + // simulate --help + $r = new \ReflectionObject($application); + $p = $r->getProperty('wantHelps'); + $p->setAccessible(true); + $p->setValue($application, true); + $command = $application->get('foo:bar'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input'); + } + + public function testSilentHelp() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $tester = new ApplicationTester($application); + $tester->run(array('-h' => true, '-q' => true), array('decorated' => false)); + + $this->assertEmpty($tester->getDisplay(true)); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The command "foofoo" does not exist. + */ + public function testGetInvalidCommand() + { + $application = new Application(); + $application->get('foofoo'); + } + + public function testGetNamespaces() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); + } + + public function testFindNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); + $application->add(new \Foo2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + } + + public function testFindNamespaceWithSubnamespaces() + { + $application = new Application(); + $application->add(new \FooSubnamespaced1Command()); + $application->add(new \FooSubnamespaced2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1). + */ + public function testFindAmbiguousNamespace() + { + $application = new Application(); + $application->add(new \BarBucCommand()); + $application->add(new \FooCommand()); + $application->add(new \Foo2Command()); + $application->findNamespace('f'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage There are no commands defined in the "bar" namespace. + */ + public function testFindInvalidNamespace() + { + $application = new Application(); + $application->findNamespace('bar'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo1" is not defined + */ + public function testFindUniqueNameButNamespaceName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($commandName = 'foo1'); + } + + public function testFind() + { + $application = new Application(); + $application->add(new \FooCommand()); + + $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); + } + + /** + * @dataProvider provideAmbiguousAbbreviations + */ + public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) + { + $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage); + + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($abbreviation); + } + + public function provideAmbiguousAbbreviations() + { + return array( + array('f', 'Command "f" is not defined.'), + array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'), + array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'), + ); + } + + public function testFindCommandEqualNamespace() + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name'); + $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name'); + } + + public function testFindCommandWithAmbiguousNamespacesButUniqueName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \FoobarCommand()); + + $this->assertInstanceOf('FoobarCommand', $application->find('f:f')); + } + + public function testFindCommandWithMissingNamespace() + { + $application = new Application(); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo4Command', $application->find('f::t')); + } + + /** + * @dataProvider provideInvalidCommandNamesSingle + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Did you mean this + */ + public function testFindAlternativeExceptionMessageSingle($name) + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->find($name); + } + + public function provideInvalidCommandNamesSingle() + { + return array( + array('foo3:baR'), + array('foO3:bar'), + ); + } + + public function testFindAlternativeExceptionMessageMultiple() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + // Command + plural + try { + $application->find('foo:baR'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1:bar/', $e->getMessage()); + $this->assertRegExp('/foo:bar/', $e->getMessage()); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1/', $e->getMessage()); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); + $this->assertRegExp('/foo3:bar/', $e->getMessage()); + $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); + } + } + + public function testFindAlternativeCommands() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find($commandName = 'Unknown command'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); + } + + // Test if "bar1" command throw a "CommandNotFoundException" and does not contain + // "foo:bar" as alternative because "bar1" is too far from "foo:bar" + try { + $application->find($commandName = 'bar1'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array('afoobar1', 'foo:bar1'), $e->getAlternatives()); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); + $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); + } + } + + public function testFindAlternativeCommandsWithAnAlias() + { + $fooCommand = new \FooCommand(); + $fooCommand->setAliases(array('foo2')); + + $application = new Application(); + $application->add($fooCommand); + + $result = $application->find('foo'); + + $this->assertSame($fooCommand, $result); + } + + public function testFindAlternativeNamespace() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \foo3Command()); + + try { + $application->find('Unknown-namespace:Unknown-command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertCount(3, $e->getAlternatives()); + $this->assertContains('foo', $e->getAlternatives()); + $this->assertContains('foo1', $e->getAlternatives()); + $this->assertContains('foo3', $e->getAlternatives()); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"'); + } + } + + public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces')); + $application->expects($this->once()) + ->method('getNamespaces') + ->will($this->returnValue(array('foo:sublong', 'bar:sub'))); + + $this->assertEquals('foo:sublong', $application->findNamespace('f:sub')); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo::bar" is not defined. + */ + public function testFindWithDoubleColonInNameThrowsException() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo4Command()); + $application->find('foo::bar'); + } + + public function testSetCatchExceptions() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $application->setCatchExceptions(true); + $this->assertTrue($application->areExceptionsCaught()); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->setCatchExceptions() sets the catch exception flag'); + $this->assertSame('', $tester->getDisplay(true)); + + $application->setCatchExceptions(false); + try { + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->fail('->setCatchExceptions() sets the catch exception flag'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); + $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); + } + } + + public function testAutoExitSetting() + { + $application = new Application(); + $this->assertTrue($application->isAutoExitEnabled()); + + $application->setAutoExit(false); + $this->assertFalse($application->isAutoExitEnabled()); + } + + public function testRenderException() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception'); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true)); + $this->assertContains('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + + $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getErrorOutput(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command()); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + $this->assertRegExp('/\[Exception\]\s*First exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is default and verbosity is verbose'); + $this->assertRegExp('/\[Exception\]\s*Second exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is 0 and verbosity is verbose'); + $this->assertRegExp('/\[Exception \(404\)\]\s*Third exception/', $tester->getDisplay(), '->renderException() renders a pretty exception with code exception when code exception is 404 and verbosity is verbose'); + + $tester->run(array('command' => 'foo3:bar'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo3:bar'), array('decorated' => true, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRenderExceptionWithDoubleWidthCharacters() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $application->register('foo')->setCode(function () { + throw new \Exception('エラーメッセージ'); + }); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo'), array('decorated' => true, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $application->register('foo')->setCode(function () { + throw new \Exception('コマンドの実行中にエラーが発生しました。'); + }); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRun() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command = new \Foo1Command()); + $_SERVER['argv'] = array('cli.php', 'foo:bar1'); + + ob_start(); + $application->run(); + ob_end_clean(); + + $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given'); + $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $this->ensureStaticCommandHelp($application); + $tester = new ApplicationTester($application); + + $tester->run(array(), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed'); + + $tester->run(array('--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed'); + + $tester->run(array('-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed'); + + $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); + + $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); + + $tester->run(array('--ansi' => true)); + $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); + + $tester->run(array('--no-ansi' => true)); + $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); + + $tester->run(array('--version' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed'); + + $tester->run(array('-V' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed'); + + $tester->run(array('command' => 'list', '--quiet' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); + + $tester->run(array('command' => 'list', '-q' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); + + $tester->run(array('command' => 'list', '--verbose' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 1)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 2)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 3)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 4)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed'); + + $tester->run(array('command' => 'list', '-v' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vv' => true)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vvv' => true)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); + + $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); + } + + /** + * Issue #9285. + * + * If the "verbose" option is just before an argument in ArgvInput, + * an argument value should not be treated as verbosity value. + * This test will fail with "Not enough arguments." if broken + */ + public function testVerboseValueNotBreakArguments() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + + $input = new ArgvInput(array('cli.php', '-v', 'foo:bar')); + $application->run($input, $output); + + $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar')); + $application->run($input, $output); + } + + public function testRunReturnsIntegerExitCode() + { + $exception = new \Exception('', 4); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); + } + + public function testRunReturnsExitCodeOneForExceptionCodeZero() + { + $exception = new \Exception('', 0); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "e" already exists. + */ + public function testAddingOptionWithDuplicateShortcut() + { + $dispatcher = new EventDispatcher(); + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->setDispatcher($dispatcher); + + $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment')); + + $application + ->register('foo') + ->setAliases(array('f')) + ->setDefinition(array(new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.'))) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + + $application->run($input, $output); + } + + /** + * @expectedException \LogicException + * @dataProvider getAddingAlreadySetDefinitionElementData + */ + public function testAddingAlreadySetDefinitionElementData($def) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application + ->register('foo') + ->setDefinition(array($def)) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + $application->run($input, $output); + } + + public function getAddingAlreadySetDefinitionElementData() + { + return array( + array(new InputArgument('command', InputArgument::REQUIRED)), + array(new InputOption('quiet', '', InputOption::VALUE_NONE)), + array(new InputOption('query', 'q', InputOption::VALUE_NONE)), + ); + } + + public function testGetDefaultHelperSetReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + } + + public function testAddingSingleHelperSetOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testOverwritingDefaultHelperSetOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testGetDefaultInputDefinitionReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + $this->assertTrue($inputDefinition->hasArgument('command')); + + $this->assertTrue($inputDefinition->hasOption('help')); + $this->assertTrue($inputDefinition->hasOption('quiet')); + $this->assertTrue($inputDefinition->hasOption('verbose')); + $this->assertTrue($inputDefinition->hasOption('version')); + $this->assertTrue($inputDefinition->hasOption('ansi')); + $this->assertTrue($inputDefinition->hasOption('no-ansi')); + $this->assertTrue($inputDefinition->hasOption('no-interaction')); + } + + public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testSettingCustomInputDefinitionOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')))); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testRunWithDispatcher() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher($this->getDispatcher()); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage caught + */ + public function testRunWithExceptionAndDispatcher() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + } + + public function testRunDispatchesAllEventsWithException() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertContains('before.foo.caught.after.', $tester->getDisplay()); + } + + public function testRunWithDispatcherSkippingCommand() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher(true)); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $exitCode = $tester->run(array('command' => 'foo')); + $this->assertContains('before.after.', $tester->getDisplay()); + $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode); + } + + public function testRunWithDispatcherAccessingInputOptions() + { + $noInteractionValue = null; + $quietValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) { + $input = $event->getInput(); + + $noInteractionValue = $input->getOption('no-interaction'); + $quietValue = $input->getOption('quiet'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--no-interaction' => true)); + + $this->assertTrue($noInteractionValue); + $this->assertFalse($quietValue); + } + + public function testRunWithDispatcherAddingInputOptions() + { + $extraValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) { + $definition = $event->getCommand()->getDefinition(); + $input = $event->getInput(); + + $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED)); + $input->bind($definition); + + $extraValue = $input->getOption('extra'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--extra' => 'some test value')); + + $this->assertEquals('some test value', $extraValue); + } + + public function testTerminalDimensions() + { + $application = new Application(); + $originalDimensions = $application->getTerminalDimensions(); + $this->assertCount(2, $originalDimensions); + + $width = 80; + if ($originalDimensions[0] == $width) { + $width = 100; + } + + $application->setTerminalDimensions($width, 80); + $this->assertSame(array($width, 80), $application->getTerminalDimensions()); + } + + protected function getDispatcher($skipCommand = false) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) { + $event->getOutput()->write('before.'); + + if ($skipCommand) { + $event->disableCommand(); + } + }); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) { + $event->getOutput()->writeln('after.'); + + if (!$skipCommand) { + $event->setExitCode(113); + } + }); + $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { + $event->getOutput()->write('caught.'); + + $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException())); + }); + + return $dispatcher; + } + + public function testSetRunCustomDefaultCommand() + { + $command = new \FooCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName()); + + $tester = new ApplicationTester($application); + $tester->run(array()); + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array()); + + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + } + + /** + * @requires function posix_isatty + */ + public function testCanCheckIfTerminalIsInteractive() + { + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'help')); + + $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n'))); + + $inputStream = $application->getHelperSet()->get('question')->getInputStream(); + $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); + } +} + +class CustomApplication extends Application +{ + /** + * Overwrites the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array(new FormatterHelper())); + } +} + +class CustomDefaultCommandApplication extends Application +{ + /** + * Overwrites the constructor in order to set a different default command. + */ + public function __construct() + { + parent::__construct(); + + $command = new \FooCommand(); + $this->add($command); + $this->setDefaultCommand($command->getName()); + } +} diff --git a/vendor/symfony/console/Tests/Command/CommandTest.php b/vendor/symfony/console/Tests/Command/CommandTest.php new file mode 100644 index 00000000000..53a6009e6c1 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/CommandTest.php @@ -0,0 +1,360 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + require_once self::$fixturesPath.'/TestCommand.php'; + } + + public function testConstructor() + { + $command = new Command('foo:bar'); + $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name. + */ + public function testCommandNameCannotBeEmpty() + { + new Command(); + } + + public function testSetApplication() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); + } + + public function testSetGetDefinition() + { + $command = new \TestCommand(); + $ret = $command->setDefinition($definition = new InputDefinition()); + $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); + $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); + $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar'))); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $command->setDefinition(new InputDefinition()); + } + + public function testAddArgument() + { + $command = new \TestCommand(); + $ret = $command->addArgument('foo'); + $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); + } + + public function testAddOption() + { + $command = new \TestCommand(); + $ret = $command->addOption('foo'); + $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); + } + + public function testGetNamespaceGetNameSetName() + { + $command = new \TestCommand(); + $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); + $command->setName('foo'); + $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); + + $ret = $command->setName('foobar:bar'); + $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); + $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); + } + + /** + * @dataProvider provideInvalidCommandNames + */ + public function testInvalidCommandNames($name) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name)); + + $command = new \TestCommand(); + $command->setName($name); + } + + public function provideInvalidCommandNames() + { + return array( + array(''), + array('foo:'), + ); + } + + public function testGetSetDescription() + { + $command = new \TestCommand(); + $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); + $ret = $command->setDescription('description1'); + $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); + $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); + } + + public function testGetSetHelp() + { + $command = new \TestCommand(); + $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); + $ret = $command->setHelp('help1'); + $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); + $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); + $command->setHelp(''); + $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description'); + } + + public function testGetProcessedHelp() + { + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); + + $command = new \TestCommand(); + $command->setHelp(''); + $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description'); + } + + public function testGetSetAliases() + { + $command = new \TestCommand(); + $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases'); + $ret = $command->setAliases(array('name1')); + $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); + $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases'); + } + + public function testGetSynopsis() + { + $command = new \TestCommand(); + $command->addOption('foo'); + $command->addArgument('bar'); + $this->assertEquals('namespace:name [--foo] [--] []', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + } + + public function testGetHelper() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined. + */ + public function testGetHelperWithoutHelperSet() + { + $command = new \TestCommand(); + $command->getHelper('formatter'); + } + + public function testMergeApplicationDefinition() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo')))); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); + + $m->invoke($command); + $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); + } + + public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array())); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command, false); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options'); + $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments'); + + $m->invoke($command, true); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments'); + + $m->invoke($command); + $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments'); + } + + public function testRunInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => true)); + + $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); + } + + public function testRunNonInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => false)); + + $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must override the execute() method in the concrete command class. + */ + public function testExecuteMethodNeedsToBeOverridden() + { + $command = new Command('foo'); + $command->run(new StringInput(''), new NullOutput()); + } + + /** + * @expectedException Symfony\Component\Console\Exception\InvalidOptionException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testRunWithInvalidOption() + { + $command = new \TestCommand(); + $tester = new CommandTester($command); + $tester->execute(array('--bar' => true)); + } + + public function testRunReturnsIntegerExitCode() + { + $command = new \TestCommand(); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); + + $command = $this->getMock('TestCommand', array('execute')); + $command->expects($this->once()) + ->method('execute') + ->will($this->returnValue('2.3')); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); + } + + public function testRunWithApplication() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + + $this->assertSame(0, $exitCode, '->run() returns an integer exit code'); + } + + public function testRunReturnsAlwaysInteger() + { + $command = new \TestCommand(); + + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + } + + public function testSetCode() + { + $command = new \TestCommand(); + $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { + $output->writeln('from the code...'); + }); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function getSetCodeBindToClosureTests() + { + return array( + array(true, 'not bound to the command'), + array(false, 'bound to the command'), + ); + } + + /** + * @dataProvider getSetCodeBindToClosureTests + */ + public function testSetCodeBindToClosure($previouslyBound, $expected) + { + $code = createClosure(); + if ($previouslyBound) { + $code = $code->bindTo($this); + } + + $command = new \TestCommand(); + $command->setCode($code); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); + } + + public function testSetCodeWithNonClosureCallable() + { + $command = new \TestCommand(); + $ret = $command->setCode(array($this, 'callableMethodCommand')); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } +} + +// In order to get an unbound closure, we should create it outside a class +// scope. +function createClosure() +{ + return function (InputInterface $input, OutputInterface $output) { + $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); + }; +} diff --git a/vendor/symfony/console/Tests/Command/HelpCommandTest.php b/vendor/symfony/console/Tests/Command/HelpCommandTest.php new file mode 100644 index 00000000000..10bb32419d3 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/HelpCommandTest.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 Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Application; + +class HelpCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteForCommandAlias() + { + $command = new HelpCommand(); + $command->setApplication(new Application()); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command_name' => 'li'), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + } + + public function testExecuteForCommand() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array(), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForCommandWithXmlOption() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array('--format' => 'xml')); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --xml is passed'); + } + + public function testExecuteForApplicationCommand() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list')); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForApplicationCommandWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list', '--format' => 'xml')); + $this->assertContains('list [--raw] [--format FORMAT] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --format=xml is passed'); + } +} diff --git a/vendor/symfony/console/Tests/Command/ListCommandTest.php b/vendor/symfony/console/Tests/Command/ListCommandTest.php new file mode 100644 index 00000000000..a166a040996 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/ListCommandTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Application; + +class ListCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteListsCommands() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + + $this->assertRegExp('/help\s{2,}Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); + } + + public function testExecuteListsCommandsWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--format' => 'xml')); + $this->assertRegExp('//', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed'); + } + + public function testExecuteListsCommandsWithRawOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsWithNamespaceArgument() + { + require_once realpath(__DIR__.'/../Fixtures/FooCommand.php'); + $application = new Application(); + $application->add(new \FooCommand()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), 'namespace' => 'foo', '--raw' => true)); + $output = <<<'EOF' +foo:bar The foo:bar command + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsOrder() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + $output = <<<'EOF' +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands + 0foo + 0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } + + public function testExecuteListsCommandsOrderRaw() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands +0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php new file mode 100644 index 00000000000..c36c4a8e5e8 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\BufferedOutput; + +abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getDescribeInputArgumentTestData */ + public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) + { + $this->assertDescription($expectedDescription, $argument); + } + + /** @dataProvider getDescribeInputOptionTestData */ + public function testDescribeInputOption(InputOption $option, $expectedDescription) + { + $this->assertDescription($expectedDescription, $option); + } + + /** @dataProvider getDescribeInputDefinitionTestData */ + public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition); + } + + /** @dataProvider getDescribeCommandTestData */ + public function testDescribeCommand(Command $command, $expectedDescription) + { + $this->assertDescription($expectedDescription, $command); + } + + /** @dataProvider getDescribeApplicationTestData */ + public function testDescribeApplication(Application $application, $expectedDescription) + { + // Replaces the dynamic placeholders of the command help text with a static version. + // The placeholder %command.full_name% includes the script path that is not predictable + // and can not be tested against. + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + + $this->assertDescription($expectedDescription, $application); + } + + public function getDescribeInputArgumentTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputArguments()); + } + + public function getDescribeInputOptionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputOptions()); + } + + public function getDescribeInputDefinitionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions()); + } + + public function getDescribeCommandTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getCommands()); + } + + public function getDescribeApplicationTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getApplications()); + } + + abstract protected function getDescriptor(); + + abstract protected function getFormat(); + + private function getDescriptionTestData(array $objects) + { + $data = array(); + foreach ($objects as $name => $object) { + $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat())); + $data[] = array($object, $description); + } + + return $data; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php new file mode 100644 index 00000000000..f9a15612b3f --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Output\BufferedOutput; + +class JsonDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new JsonDescriptor(); + } + + protected function getFormat() + { + return 'json'; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(json_decode(trim($expectedDescription), true), json_decode(trim(str_replace(PHP_EOL, "\n", $output->fetch())), true)); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php new file mode 100644 index 00000000000..c85e8a594be --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; + +class MarkdownDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new MarkdownDescriptor(); + } + + protected function getFormat() + { + return 'md'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php new file mode 100644 index 00000000000..45b3b2fff90 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.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 Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication2; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand2; + +/** + * @author Jean-François Simon + */ +class ObjectsProvider +{ + public static function getInputArguments() + { + return array( + 'input_argument_1' => new InputArgument('argument_name', InputArgument::REQUIRED), + 'input_argument_2' => new InputArgument('argument_name', InputArgument::IS_ARRAY, 'argument description'), + 'input_argument_3' => new InputArgument('argument_name', InputArgument::OPTIONAL, 'argument description', 'default_value'), + 'input_argument_4' => new InputArgument('argument_name', InputArgument::REQUIRED, "multiline\nargument description"), + ); + } + + public static function getInputOptions() + { + return array( + 'input_option_1' => new InputOption('option_name', 'o', InputOption::VALUE_NONE), + 'input_option_2' => new InputOption('option_name', 'o', InputOption::VALUE_OPTIONAL, 'option description', 'default_value'), + 'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'), + 'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()), + 'input_option_5' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, "multiline\noption description"), + 'input_option_6' => new InputOption('option_name', array('o', 'O'), InputOption::VALUE_REQUIRED, 'option with multiple shortcuts'), + ); + } + + public static function getInputDefinitions() + { + return array( + 'input_definition_1' => new InputDefinition(), + 'input_definition_2' => new InputDefinition(array(new InputArgument('argument_name', InputArgument::REQUIRED))), + 'input_definition_3' => new InputDefinition(array(new InputOption('option_name', 'o', InputOption::VALUE_NONE))), + 'input_definition_4' => new InputDefinition(array( + new InputArgument('argument_name', InputArgument::REQUIRED), + new InputOption('option_name', 'o', InputOption::VALUE_NONE), + )), + ); + } + + public static function getCommands() + { + return array( + 'command_1' => new DescriptorCommand1(), + 'command_2' => new DescriptorCommand2(), + ); + } + + public static function getApplications() + { + return array( + 'application_1' => new DescriptorApplication1(), + 'application_2' => new DescriptorApplication2(), + ); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php new file mode 100644 index 00000000000..350b67950d2 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\TextDescriptor; + +class TextDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new TextDescriptor(); + } + + protected function getFormat() + { + return 'txt'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php new file mode 100644 index 00000000000..59a5d1ed8a2 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\XmlDescriptor; + +class XmlDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new XmlDescriptor(); + } + + protected function getFormat() + { + return 'xml'; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php new file mode 100644 index 00000000000..52b619e821e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php @@ -0,0 +1,11 @@ +setName('bar:buc'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php new file mode 100644 index 00000000000..132b6d57dda --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication1 extends Application +{ +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php new file mode 100644 index 00000000000..ff551358004 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication2 extends Application +{ + public function __construct() + { + parent::__construct('My Symfony application', 'v1.0'); + $this->add(new DescriptorCommand1()); + $this->add(new DescriptorCommand2()); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php new file mode 100644 index 00000000000..ede05d7a73c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; + +class DescriptorCommand1 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command1') + ->setAliases(array('alias1', 'alias2')) + ->setDescription('command 1 description') + ->setHelp('command 1 help') + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php new file mode 100644 index 00000000000..51106b96116 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.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 Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class DescriptorCommand2 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command2') + ->setDescription('command 2 description') + ->setHelp('command 2 help') + ->addUsage('-o|--option_name ') + ->addUsage('') + ->addArgument('argument_name', InputArgument::REQUIRED) + ->addOption('option_name', 'o', InputOption::VALUE_NONE) + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DummyOutput.php b/vendor/symfony/console/Tests/Fixtures/DummyOutput.php new file mode 100644 index 00000000000..0070c0a4867 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DummyOutput.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 Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Output\BufferedOutput; + +/** + * Dummy output. + * + * @author Kévin Dunglas + */ +class DummyOutput extends BufferedOutput +{ + /** + * @return array + */ + public function getLogs() + { + $logs = array(); + foreach (explode("\n", trim($this->fetch())) as $message) { + preg_match('/^\[(.*)\] (.*)/', $message, $matches); + $logs[] = sprintf('%s %s', $matches[1], $matches[2]); + } + + return $logs; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo1Command.php b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php new file mode 100644 index 00000000000..254162f320d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar1') + ->setDescription('The foo:bar1 command') + ->setAliases(array('afoobar1')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo2Command.php b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php new file mode 100644 index 00000000000..8071dc8fb3d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php @@ -0,0 +1,21 @@ +setName('foo1:bar') + ->setDescription('The foo1:bar command') + ->setAliases(array('afoobar2')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo3Command.php b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php new file mode 100644 index 00000000000..adb3a2d809f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php @@ -0,0 +1,29 @@ +setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + try { + throw new \Exception('First exception

this is html

'); + } catch (\Exception $e) { + throw new \Exception('Second exception comment', 0, $e); + } + } catch (\Exception $e) { + throw new \Exception('Third exception comment', 404, $e); + } + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo4Command.php b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php new file mode 100644 index 00000000000..1c5463995f9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php @@ -0,0 +1,11 @@ +setName('foo3:bar:toh'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo5Command.php b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php new file mode 100644 index 00000000000..a1c60827a51 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php @@ -0,0 +1,10 @@ +setName('0foo:bar')->setDescription('0foo:bar command'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooCommand.php b/vendor/symfony/console/Tests/Fixtures/FooCommand.php new file mode 100644 index 00000000000..355e0ad6d63 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooCommand.php @@ -0,0 +1,33 @@ +setName('foo:bar') + ->setDescription('The foo:bar command') + ->setAliases(array('afoobar')) + ; + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $output->writeln('called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php new file mode 100644 index 00000000000..fc50c72bfcf --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar:baz') + ->setDescription('The foo:bar:baz command') + ->setAliases(array('foobarbaz')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php new file mode 100644 index 00000000000..1cf31ff110c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php @@ -0,0 +1,26 @@ +setName('foo:go:bret') + ->setDescription('The foo:bar:go command') + ->setAliases(array('foobargo')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php new file mode 100644 index 00000000000..968162804ce --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php @@ -0,0 +1,25 @@ +setName('foobar:foo') + ->setDescription('The foobar:foo command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php new file mode 100644 index 00000000000..996fafb989c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php @@ -0,0 +1,11 @@ +caution('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php new file mode 100644 index 00000000000..6634cd56905 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php @@ -0,0 +1,13 @@ +title('Title'); + $output->warning('Lorem ipsum dolor sit amet'); + $output->title('Title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php new file mode 100644 index 00000000000..4120df9cb6b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php @@ -0,0 +1,17 @@ +block( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + 'CUSTOM', + 'fg=white;bg=green', + 'X ', + true + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php new file mode 100644 index 00000000000..678afea5d72 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php @@ -0,0 +1,12 @@ +block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php new file mode 100644 index 00000000000..4386e56de20 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php @@ -0,0 +1,13 @@ +comment( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_13.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_13.php new file mode 100644 index 00000000000..fe084365f96 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_13.php @@ -0,0 +1,15 @@ +setDecorated(true); + $output = new SymfonyStyle($input, $output); + $output->comment( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_14.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_14.php new file mode 100644 index 00000000000..e719f71812c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_14.php @@ -0,0 +1,17 @@ +block( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + null, + null, + '$ ', + true + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_15.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_15.php new file mode 100644 index 00000000000..29e555b0470 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_15.php @@ -0,0 +1,14 @@ +block( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + 'TEST' + ); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php new file mode 100644 index 00000000000..6004e3d6c29 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php @@ -0,0 +1,16 @@ +warning('Warning'); + $output->caution('Caution'); + $output->error('Error'); + $output->success('Success'); + $output->note('Note'); + $output->block('Custom block', 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php new file mode 100644 index 00000000000..c7a08f138d0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php @@ -0,0 +1,12 @@ +title('First title'); + $output->title('Second title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php new file mode 100644 index 00000000000..afea70c7aad --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php @@ -0,0 +1,34 @@ +write('Lorem ipsum dolor sit amet'); + $output->title('First title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->title('Second title'); + + $output->write('Lorem ipsum dolor sit amet'); + $output->write(''); + $output->title('Third title'); + + //Ensure edge case by appending empty strings to history: + $output->write('Lorem ipsum dolor sit amet'); + $output->write(array('', '', '')); + $output->title('Fourth title'); + + //Ensure have manual control over number of blank lines: + $output->writeln('Lorem ipsum dolor sit amet'); + $output->writeln(array('', '')); //Should append an extra blank line + $output->title('Fifth title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->newLine(2); //Should append an extra blank line + $output->title('Fifth title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php new file mode 100644 index 00000000000..d2c68a9e73a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php @@ -0,0 +1,37 @@ +writeln('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + //Even using write: + $output->write('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->write('Lorem ipsum dolor sit amet'); + $output->text(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->newLine(); + + $output->write('Lorem ipsum dolor sit amet'); + $output->comment(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php new file mode 100644 index 00000000000..f1d79905449 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php @@ -0,0 +1,16 @@ +listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + $output->success('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php new file mode 100644 index 00000000000..cbfea734b41 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php @@ -0,0 +1,15 @@ +title('Title'); + $output->askHidden('Hidden question'); + $output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1'); + $output->confirm('Confirmation with yes default', true); + $output->text('Duis aute irure dolor in reprehenderit in voluptate velit esse'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php new file mode 100644 index 00000000000..0244fd27256 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php @@ -0,0 +1,26 @@ + 3))), + array('ISBN', 'Title', 'Author'), + ); + + $rows = array( + array( + '978-0521567817', + 'De Monarchia', + new TableCell("Dante Alighieri\nspans multiple rows", array('rowspan' => 2)), + ), + array('978-0804169127', 'Divine Comedy'), + ); + + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->table($headers, $rows); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php new file mode 100644 index 00000000000..6420730fd64 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php @@ -0,0 +1,11 @@ +block(array('Custom block', 'Second custom block line'), 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt new file mode 100644 index 00000000000..a42e0f792aa --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt @@ -0,0 +1,3 @@ + + ! [CAUTION] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt new file mode 100644 index 00000000000..334875f7891 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt @@ -0,0 +1,9 @@ + +Title +===== + + [WARNING] Lorem ipsum dolor sit amet + +Title +===== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt new file mode 100644 index 00000000000..385c6a283c8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt @@ -0,0 +1,7 @@ + +X [CUSTOM] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et +X dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea +X commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat +X nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit +X anim id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt new file mode 100644 index 00000000000..190d7840354 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt @@ -0,0 +1,4 @@ + + § [CUSTOM] Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophatto + § peristeralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt new file mode 100644 index 00000000000..9983af832aa --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt @@ -0,0 +1,6 @@ + + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna + // aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + // sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt new file mode 100644 index 00000000000..0f3704b7482 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt @@ -0,0 +1,7 @@ + + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et  + // dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea  + // commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla  + // pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim + // id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_14.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_14.txt new file mode 100644 index 00000000000..1d0d37e7fe3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_14.txt @@ -0,0 +1,6 @@ + +$ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna +$ aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +$ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint +$ occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_15.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_15.txt new file mode 100644 index 00000000000..66404b8151e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_15.txt @@ -0,0 +1,7 @@ + + [TEST] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt new file mode 100644 index 00000000000..ca609760cc1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt @@ -0,0 +1,13 @@ + + [WARNING] Warning + + ! [CAUTION] Caution + + [ERROR] Error + + [OK] Success + + ! [NOTE] Note + +X [CUSTOM] Custom block + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt new file mode 100644 index 00000000000..f4b6d58276c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt @@ -0,0 +1,7 @@ + +First title +=========== + +Second title +============ + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt new file mode 100644 index 00000000000..2646d858e7c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt @@ -0,0 +1,32 @@ +Lorem ipsum dolor sit amet + +First title +=========== + +Lorem ipsum dolor sit amet + +Second title +============ + +Lorem ipsum dolor sit amet + +Third title +=========== + +Lorem ipsum dolor sit amet + +Fourth title +============ + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt new file mode 100644 index 00000000000..be4a2db6057 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt @@ -0,0 +1,18 @@ +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + Lorem ipsum dolor sit amet + consectetur adipiscing elit + +Lorem ipsum dolor sit amet + + // Lorem ipsum dolor sit amet + // + // consectetur adipiscing elit + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt new file mode 100644 index 00000000000..5f2d33c148a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt @@ -0,0 +1,6 @@ + + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + + [OK] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt new file mode 100644 index 00000000000..ecea9778b1e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt @@ -0,0 +1,5 @@ + +Title +===== + + Duis aute irure dolor in reprehenderit in voluptate velit esse diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt new file mode 100644 index 00000000000..005b846eae4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt @@ -0,0 +1,9 @@ + ---------------- --------------- --------------------- + Main table title + ---------------- --------------- --------------------- + ISBN Title Author + ---------------- --------------- --------------------- + 978-0521567817 De Monarchia Dante Alighieri + 978-0804169127 Divine Comedy spans multiple rows + ---------------- --------------- --------------------- + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt new file mode 100644 index 00000000000..069c0d5119f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt @@ -0,0 +1,5 @@ + +X [CUSTOM] Custom block +X +X Second custom block line + diff --git a/vendor/symfony/console/Tests/Fixtures/TestCommand.php b/vendor/symfony/console/Tests/Fixtures/TestCommand.php new file mode 100644 index 00000000000..dcd32739c4c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/TestCommand.php @@ -0,0 +1,28 @@ +setName('namespace:name') + ->setAliases(array('name')) + ->setDescription('description') + ->setHelp('help') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('execute called'); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.json b/vendor/symfony/console/Tests/Fixtures/application_1.json new file mode 100644 index 00000000000..b3ad97d039c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}}],"namespaces":[{"id":"_global","commands":["help","list"]}]} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.md b/vendor/symfony/console/Tests/Fixtures/application_1.md new file mode 100644 index 00000000000..f1d88c5b7d1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.md @@ -0,0 +1,181 @@ +UNKNOWN +======= + +* help +* list + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.txt b/vendor/symfony/console/Tests/Fixtures/application_1.txt new file mode 100644 index 00000000000..c4cf8f2164c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.xml b/vendor/symfony/console/Tests/Fixtures/application_1.xml new file mode 100644 index 00000000000..8514f233be6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.xml @@ -0,0 +1,104 @@ + + + + + + help [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + list [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + + help + list + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.json b/vendor/symfony/console/Tests/Fixtures/application_2.json new file mode 100644 index 00000000000..e8870ba35dc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}},{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":{"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}}],"namespaces":[{"id":"_global","commands":["alias1","alias2","help","list"]},{"id":"descriptor","commands":["descriptor:command1","descriptor:command2"]}]} \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.md b/vendor/symfony/console/Tests/Fixtures/application_2.md new file mode 100644 index 00000000000..63b2d9ab422 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.md @@ -0,0 +1,376 @@ +My Symfony application +====================== + +* alias1 +* alias2 +* help +* list + +**descriptor:** + +* descriptor:command1 +* descriptor:command2 + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help + +### Options: + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.txt b/vendor/symfony/console/Tests/Fixtures/application_2.txt new file mode 100644 index 00000000000..292aa829d80 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.txt @@ -0,0 +1,22 @@ +My Symfony application version v1.0 + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + alias1 command 1 description + alias2 command 1 description + help Displays help for a command + list Lists commands + descriptor + descriptor:command1 command 1 description + descriptor:command2 command 2 description diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.xml b/vendor/symfony/console/Tests/Fixtures/application_2.xml new file mode 100644 index 00000000000..62e3cfcbe05 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.xml @@ -0,0 +1,184 @@ + + + + + + help [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + list [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + + + + + + + + + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + + + + + + + + + + + + alias1 + alias2 + help + list + + + descriptor:command1 + descriptor:command2 + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext1.txt b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt new file mode 100644 index 00000000000..19dacb23bf7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt @@ -0,0 +1,20 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + afoobar The foo:bar command + help Displays help for a command + list Lists commands + foo + foo:bar The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext2.txt b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt new file mode 100644 index 00000000000..c99ccdda7a6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt @@ -0,0 +1,16 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands for the "foo" namespace: + foo:bar The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt new file mode 100644 index 00000000000..0c16e3c84be --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt @@ -0,0 +1 @@ +Console Tool \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt new file mode 100644 index 00000000000..919cec4214a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt @@ -0,0 +1,6 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not defined. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt new file mode 100644 index 00000000000..64561715e04 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt @@ -0,0 +1,8 @@ + + + [Symfony\Component\Console\Exception\InvalidOptionException] + The "--foo" option does not exist. + + +list [--raw] [--format FORMAT] [--] [] + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt new file mode 100644 index 00000000000..8276137bd88 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt @@ -0,0 +1,18 @@ + + + [Exception] + Third exception comment + + + + [Exception] + Second exception comment + + + + [Exception] + First exception

this is html

+ + +foo3:bar + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt new file mode 100644 index 00000000000..b4a7b018af3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt @@ -0,0 +1,18 @@ + +  + [Exception]  + Third exception comment  +  + +  + [Exception]  + Second exception comment  +  + +  + [Exception]  + First exception 

this is html

  +  + +foo3:bar + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt new file mode 100644 index 00000000000..cb080e9cb53 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt @@ -0,0 +1,7 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not define + d. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt new file mode 100644 index 00000000000..1ba5f8fdd91 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt @@ -0,0 +1,8 @@ + + + [Exception] + エラーメッセージ + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt new file mode 100644 index 00000000000..20644251c2f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt @@ -0,0 +1,8 @@ + +  + [Exception]  + エラーメッセージ  +  + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt new file mode 100644 index 00000000000..e41fcfcf675 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt @@ -0,0 +1,9 @@ + + + [Exception] + コマンドの実行中にエラーが + 発生しました。 + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_run1.txt b/vendor/symfony/console/Tests/Fixtures/application_run1.txt new file mode 100644 index 00000000000..0dc2730983f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_run2.txt b/vendor/symfony/console/Tests/Fixtures/application_run2.txt new file mode 100644 index 00000000000..0098e9aa269 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run2.txt @@ -0,0 +1,28 @@ +Usage: + help [options] [--] [] + +Arguments: + command The command to execute + command_name The command name [default: "help"] + +Options: + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + --raw To output raw command help + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + The help command displays help for a given command: + + php app/console help list + + You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + + To display the list of available commands, please use the list command. diff --git a/vendor/symfony/console/Tests/Fixtures/application_run3.txt b/vendor/symfony/console/Tests/Fixtures/application_run3.txt new file mode 100644 index 00000000000..fe566d76bae --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run3.txt @@ -0,0 +1,26 @@ +Usage: + list [options] [--] [] + +Arguments: + namespace The namespace name + +Options: + --raw To output raw command list + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + +Help: + The list command lists all commands: + + php app/console list + + You can also display the commands for a specific namespace: + + php app/console list test + + You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + + It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw diff --git a/vendor/symfony/console/Tests/Fixtures/application_run4.txt b/vendor/symfony/console/Tests/Fixtures/application_run4.txt new file mode 100644 index 00000000000..47187fc2673 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run4.txt @@ -0,0 +1 @@ +Console Tool diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.json b/vendor/symfony/console/Tests/Fixtures/command_1.json new file mode 100644 index 00000000000..20f310b457e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.json @@ -0,0 +1 @@ +{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":[]}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.md b/vendor/symfony/console/Tests/Fixtures/command_1.md new file mode 100644 index 00000000000..34ed3ea7709 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.md @@ -0,0 +1,11 @@ +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.txt b/vendor/symfony/console/Tests/Fixtures/command_1.txt new file mode 100644 index 00000000000..28e14a057a7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.txt @@ -0,0 +1,7 @@ +Usage: + descriptor:command1 + alias1 + alias2 + +Help: + command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.xml b/vendor/symfony/console/Tests/Fixtures/command_1.xml new file mode 100644 index 00000000000..838b9bd91f8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.xml @@ -0,0 +1,12 @@ + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.json b/vendor/symfony/console/Tests/Fixtures/command_2.json new file mode 100644 index 00000000000..38edd1e2098 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.json @@ -0,0 +1 @@ +{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.md b/vendor/symfony/console/Tests/Fixtures/command_2.md new file mode 100644 index 00000000000..6f538b64079 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.md @@ -0,0 +1,33 @@ +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.txt b/vendor/symfony/console/Tests/Fixtures/command_2.txt new file mode 100644 index 00000000000..72f7ce05869 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.txt @@ -0,0 +1,13 @@ +Usage: + descriptor:command2 [options] [--] + descriptor:command2 -o|--option_name + descriptor:command2 + +Arguments: + argument_name + +Options: + -o, --option_name + +Help: + command 2 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.xml b/vendor/symfony/console/Tests/Fixtures/command_2.xml new file mode 100644 index 00000000000..67364caa4ed --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.xml @@ -0,0 +1,21 @@ + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/command_astext.txt b/vendor/symfony/console/Tests/Fixtures/command_astext.txt new file mode 100644 index 00000000000..7e206388078 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_astext.txt @@ -0,0 +1,18 @@ +Usage: + namespace:name + name + +Arguments: + command The command to execute + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + help diff --git a/vendor/symfony/console/Tests/Fixtures/command_asxml.txt b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt new file mode 100644 index 00000000000..5e776238aa0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt @@ -0,0 +1,38 @@ + + + + namespace:name + name + + description + help + + + The command to execute + + + + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/definition_astext.txt b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt new file mode 100644 index 00000000000..0431c072ab2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt @@ -0,0 +1,11 @@ +Arguments: + foo The foo argument + baz The baz argument [default: true] + bar The bar argument [default: ["http://foo.com/"]] + +Options: + -f, --foo=FOO The foo option + --baz[=BAZ] The baz option [default: false] + -b, --bar[=BAR] The bar option [default: "bar"] + --qux[=QUX] The qux option [default: ["http://foo.com/","bar"]] (multiple values allowed) + --qux2[=QUX2] The qux2 option [default: {"foo":"bar"}] (multiple values allowed) \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt new file mode 100644 index 00000000000..eec8c079ee3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt @@ -0,0 +1,39 @@ + + + + + The foo argument + + + + The baz argument + + true + + + + The bar argument + + bar + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.json b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json new file mode 100644 index 00000000000..b8173b6b3fc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.md b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md new file mode 100644 index 00000000000..88f311ab534 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt new file mode 100644 index 00000000000..55035183f67 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt @@ -0,0 +1 @@ + argument_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml new file mode 100644 index 00000000000..cb37f812c5e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.json b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json new file mode 100644 index 00000000000..ef06b09a755 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":true,"description":"argument description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.md b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md new file mode 100644 index 00000000000..3cdb00cc818 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: yes +* Description: argument description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt new file mode 100644 index 00000000000..e713660743f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt @@ -0,0 +1 @@ + argument_name argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml new file mode 100644 index 00000000000..629da5a98a3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml @@ -0,0 +1,5 @@ + + + argument description + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.json b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json new file mode 100644 index 00000000000..de8484e6a72 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":false,"description":"argument description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.md b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md new file mode 100644 index 00000000000..be1c443ae0d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: no +* Description: argument description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt new file mode 100644 index 00000000000..6b76639e001 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt @@ -0,0 +1 @@ + argument_name argument description [default: "default_value"] diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml new file mode 100644 index 00000000000..399a5c864d5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml @@ -0,0 +1,7 @@ + + + argument description + + default_value + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.json b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json new file mode 100644 index 00000000000..8067a4d1b12 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"multiline argument description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.md b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md new file mode 100644 index 00000000000..f026ab37672 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md @@ -0,0 +1,8 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: multiline + argument description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt new file mode 100644 index 00000000000..aa74e8ceb25 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt @@ -0,0 +1,2 @@ + argument_name multiline + argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml new file mode 100644 index 00000000000..5ca135ec2cb --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml @@ -0,0 +1,6 @@ + + + multiline +argument description + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.json b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json new file mode 100644 index 00000000000..c7a7d838fd5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json @@ -0,0 +1 @@ +{"arguments":[],"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.md b/vendor/symfony/console/Tests/Fixtures/input_definition_1.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml new file mode 100644 index 00000000000..b5481ce1276 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.json b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json new file mode 100644 index 00000000000..9964a55ae31 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.md b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md new file mode 100644 index 00000000000..923191cdcf9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md @@ -0,0 +1,9 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt new file mode 100644 index 00000000000..73b0f308a90 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt @@ -0,0 +1,2 @@ +Arguments: + argument_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml new file mode 100644 index 00000000000..102efc14863 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.json b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json new file mode 100644 index 00000000000..6a860560297 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json @@ -0,0 +1 @@ +{"arguments":[],"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.md b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md new file mode 100644 index 00000000000..40fd7b0a973 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md @@ -0,0 +1,11 @@ +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt new file mode 100644 index 00000000000..c02766fd3c1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt @@ -0,0 +1,2 @@ +Options: + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml new file mode 100644 index 00000000000..bc95151548a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.json b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json new file mode 100644 index 00000000000..c5a0019fe2a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.md b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md new file mode 100644 index 00000000000..a31feea477c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md @@ -0,0 +1,21 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt new file mode 100644 index 00000000000..63aa81d2d16 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt @@ -0,0 +1,5 @@ +Arguments: + argument_name + +Options: + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml new file mode 100644 index 00000000000..cffceecef14 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.json b/vendor/symfony/console/Tests/Fixtures/input_option_1.json new file mode 100644 index 00000000000..60c5b56cb44 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.md b/vendor/symfony/console/Tests/Fixtures/input_option_1.md new file mode 100644 index 00000000000..6f9e9a7e074 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.txt b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt new file mode 100644 index 00000000000..3a5e4eede48 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt @@ -0,0 +1 @@ + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.xml b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml new file mode 100644 index 00000000000..8a64ea65296 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml @@ -0,0 +1,4 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.json b/vendor/symfony/console/Tests/Fixtures/input_option_2.json new file mode 100644 index 00000000000..04e4228ec31 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":false,"description":"option description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.md b/vendor/symfony/console/Tests/Fixtures/input_option_2.md new file mode 100644 index 00000000000..634ac0b03c0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: no +* Description: option description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.txt b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt new file mode 100644 index 00000000000..1009eff162e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description [default: "default_value"] diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.xml b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml new file mode 100644 index 00000000000..4afac5b04e1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml @@ -0,0 +1,7 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.json b/vendor/symfony/console/Tests/Fixtures/input_option_3.json new file mode 100644 index 00000000000..c1ea120c7a2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.md b/vendor/symfony/console/Tests/Fixtures/input_option_3.md new file mode 100644 index 00000000000..34282896ba3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.txt b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt new file mode 100644 index 00000000000..947bb6527bb --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt @@ -0,0 +1 @@ + -o, --option_name=OPTION_NAME option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.xml b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml new file mode 100644 index 00000000000..dcc0631cf4e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.json b/vendor/symfony/console/Tests/Fixtures/input_option_4.json new file mode 100644 index 00000000000..1b671d80651 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":true,"description":"option description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.md b/vendor/symfony/console/Tests/Fixtures/input_option_4.md new file mode 100644 index 00000000000..8ffba56e732 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: yes +* Description: option description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.txt b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt new file mode 100644 index 00000000000..27edf77b4b8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description (multiple values allowed) diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.xml b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml new file mode 100644 index 00000000000..5e2418b14ad --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.json b/vendor/symfony/console/Tests/Fixtures/input_option_5.json new file mode 100644 index 00000000000..35a1405fa43 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"multiline option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.md b/vendor/symfony/console/Tests/Fixtures/input_option_5.md new file mode 100644 index 00000000000..82f51cad30d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.md @@ -0,0 +1,10 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: multiline + option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.txt b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt new file mode 100644 index 00000000000..4368883cc72 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt @@ -0,0 +1,2 @@ + -o, --option_name=OPTION_NAME multiline + option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.xml b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml new file mode 100644 index 00000000000..90040ccd029 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml @@ -0,0 +1,6 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.json b/vendor/symfony/console/Tests/Fixtures/input_option_6.json new file mode 100644 index 00000000000..d84e8721475 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o|-O","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option with multiple shortcuts","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.md b/vendor/symfony/console/Tests/Fixtures/input_option_6.md new file mode 100644 index 00000000000..ed1ea1c84bd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o|-O` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option with multiple shortcuts +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.txt b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt new file mode 100644 index 00000000000..0e6c9759b57 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt @@ -0,0 +1 @@ + -o|O, --option_name=OPTION_NAME option with multiple shortcuts diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.xml b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml new file mode 100644 index 00000000000..06126a2f570 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 00000000000..774df268ba1 --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.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 Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase +{ + public function testPush() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->getCurrent()); + + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s3, $stack->getCurrent()); + } + + public function testPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->pop()); + $this->assertEquals($s1, $stack->pop()); + } + + public function testPopEmpty() + { + $stack = new OutputFormatterStyleStack(); + $style = new OutputFormatterStyle(); + + $this->assertEquals($style, $stack->pop()); + } + + public function testPopNotLast() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s2, $stack->pop($s2)); + $this->assertEquals($s1, $stack->pop()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push(new OutputFormatterStyle('white', 'black')); + $stack->pop(new OutputFormatterStyle('yellow', 'blue')); + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php new file mode 100644 index 00000000000..0abfb3ce275 --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore')); + $this->assertEquals("\033[32;40;1;4mfoo\033[39;49;22;24m", $style->apply('foo')); + + $style = new OutputFormatterStyle('red', null, array('blink')); + $this->assertEquals("\033[31;5mfoo\033[39;25m", $style->apply('foo')); + + $style = new OutputFormatterStyle(null, 'white'); + $this->assertEquals("\033[47mfoo\033[49m", $style->apply('foo')); + } + + public function testForeground() + { + $style = new OutputFormatterStyle(); + + $style->setForeground('black'); + $this->assertEquals("\033[30mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('blue'); + $this->assertEquals("\033[34mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('default'); + $this->assertEquals("\033[39mfoo\033[39m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setForeground('undefined-color'); + } + + public function testBackground() + { + $style = new OutputFormatterStyle(); + + $style->setBackground('black'); + $this->assertEquals("\033[40mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('yellow'); + $this->assertEquals("\033[43mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('default'); + $this->assertEquals("\033[49mfoo\033[49m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setBackground('undefined-color'); + } + + public function testOptions() + { + $style = new OutputFormatterStyle(); + + $style->setOptions(array('reverse', 'conceal')); + $this->assertEquals("\033[7;8mfoo\033[27;28m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[7;8;1mfoo\033[27;28;22m", $style->apply('foo')); + + $style->unsetOption('reverse'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOptions(array('bold')); + $this->assertEquals("\033[1mfoo\033[22m", $style->apply('foo')); + + try { + $style->setOption('foo'); + $this->fail('->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + + try { + $style->unsetOption('foo'); + $this->fail('->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php new file mode 100644 index 00000000000..b8d5ca6d9be --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function testEmptyTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals('foo<>bar', $formatter->format('foo<>bar')); + } + + public function testLGCharEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals('fooformat('foo\\assertEquals('some info', $formatter->format('\\some info\\')); + $this->assertEquals('\\some info\\', OutputFormatter::escape('some info')); + + $this->assertEquals( + "\033[33mSymfony\\Component\\Console does work very well!\033[39m", + $formatter->format('Symfony\Component\Console does work very well!') + ); + } + + public function testBundledStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", + $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", + $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", + $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", + $formatter->format('some question') + ); + } + + public function testNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome \033[39;49m\033[32msome info\033[39m\033[37;41m error\033[39;49m", + $formatter->format('some some info error') + ); + } + + public function testAdjacentStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m\033[32msome info\033[39m", + $formatter->format('some errorsome info') + ); + } + + public function testStyleMatchingNotGreedy() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32m>=2.0,<2.3\033[39m)", + $formatter->format('(>=2.0,<2.3)') + ); + } + + public function testStyleEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32mz>=2.0,<<format('('.$formatter->escape('z>=2.0,<\\<)') + ); + + $this->assertEquals( + "\033[32msome error\033[39m", + $formatter->format(''.$formatter->escape('some error').'') + ); + } + + public function testDeepNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41merror\033[39;49m\033[32minfo\033[39m\033[33mcomment\033[39m\033[37;41merror\033[39;49m", + $formatter->format('errorinfocommenterror') + ); + } + + public function testNewStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('test', $style); + + $this->assertEquals($style, $formatter->getStyle('test')); + $this->assertNotEquals($style, $formatter->getStyle('info')); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('b', $style); + + $this->assertEquals("\033[34;47msome \033[39;49m\033[34;47mcustom\033[39;49m\033[34;47m msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testRedefineStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('info', $style); + + $this->assertEquals("\033[34;47msome custom msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testInlineStyle() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + } + + public function testNonStyleTag() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[32msome \033[39m\033[32m\033[39m\033[32m \033[39m\033[32m\033[39m\033[32m styled \033[39m\033[32m

\033[39m\033[32msingle-char tag\033[39m\033[32m

\033[39m", $formatter->format('some styled

single-char tag

')); + } + + public function testFormatLongString() + { + $formatter = new OutputFormatter(true); + $long = str_repeat('\\', 14000); + $this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('some error'.$long)); + } + + public function testFormatToStringObject() + { + $formatter = new OutputFormatter(false); + $this->assertEquals( + 'some info', $formatter->format(new TableCell()) + ); + } + + public function testNotDecoratedFormatter() + { + $formatter = new OutputFormatter(false); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + 'some error', $formatter->format('some error') + ); + $this->assertEquals( + 'some info', $formatter->format('some info') + ); + $this->assertEquals( + 'some comment', $formatter->format('some comment') + ); + $this->assertEquals( + 'some question', $formatter->format('some question') + ); + + $formatter->setDecorated(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", $formatter->format('some question') + ); + } + + public function testContentWithLineBreaks() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals(<<format(<<<'EOF' + +some text +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' +some text + +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' + +some text + +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' + +some text +more text + +EOF + )); + } +} + +class TableCell +{ + public function __toString() + { + return 'some info'; + } +} diff --git a/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php new file mode 100644 index 00000000000..141dc1dcbeb --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\FormatterHelper; + +class FormatterHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testFormatSection() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '[cli] Some text to display', + $formatter->formatSection('cli', 'Some text to display'), + '::formatSection() formats a message in a section' + ); + } + + public function testFormatBlock() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' Some text to display ', + $formatter->formatBlock('Some text to display', 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' Some text to display '."\n". + ' foo bar ', + $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' '."\n". + ' Some text to display '."\n". + ' ', + $formatter->formatBlock('Some text to display', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDiacriticLetters() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' Du texte à afficher '."\n". + ' ', + $formatter->formatBlock('Du texte à afficher', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDoubleWidthDiacriticLetters() + { + $formatter = new FormatterHelper(); + $this->assertEquals( + ' '."\n". + ' 表示するテキスト '."\n". + ' ', + $formatter->formatBlock('表示するテキスト', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockLGEscaping() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' \some info\ '."\n". + ' ', + $formatter->formatBlock('some info', 'error', true), + '::formatBlock() escapes \'<\' chars' + ); + } + + public function testTruncatingWithShorterLengthThanMessageWithSuffix() + { + $formatter = new FormatterHelper(); + $message = 'testing truncate'; + + $this->assertSame('test...', $formatter->truncate($message, 4)); + $this->assertSame('testing truncat...', $formatter->truncate($message, 15)); + $this->assertSame('testing truncate...', $formatter->truncate($message, 16)); + $this->assertSame('zażółć gęślą...', $formatter->truncate('zażółć gęślą jaźń', 12)); + } + + public function testTruncatingMessageWithCustomSuffix() + { + $formatter = new FormatterHelper(); + $message = 'testing truncate'; + + $this->assertSame('test!', $formatter->truncate($message, 4, '!')); + } + + public function testTruncatingWithLongerLengthThanMessageWithSuffix() + { + $formatter = new FormatterHelper(); + $message = 'test'; + + $this->assertSame($message, $formatter->truncate($message, 10)); + } + + public function testTruncatingWithNegativeLength() + { + $formatter = new FormatterHelper(); + $message = 'testing truncate'; + + $this->assertSame('testing tru...', $formatter->truncate($message, -5)); + $this->assertSame('...', $formatter->truncate($message, -100)); + } +} diff --git a/vendor/symfony/console/Tests/Helper/HelperSetTest.php b/vendor/symfony/console/Tests/Helper/HelperSetTest.php new file mode 100644 index 00000000000..04edd30411f --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/HelperSetTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Command\Command; + +class HelperSetTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $mock_helper = $this->getGenericMockHelper('fake_helper'); + $helperset = new HelperSet(array('fake_helper_alias' => $mock_helper)); + + $this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers'); + $this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper'); + } + + public function testSet() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset)); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + $this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls'); + $this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias'); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set'); + } + + public function testHas() + { + $helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper'))); + $this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias'); + } + + public function testGet() + { + $helper_01 = $this->getGenericMockHelper('fake_helper_01'); + $helper_02 = $this->getGenericMockHelper('fake_helper_02'); + $helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02)); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name'); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias'); + + $helperset = new HelperSet(); + try { + $helperset->get('foo'); + $this->fail('->get() throws InvalidArgumentException when helper not found'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws InvalidArgumentException when helper not found'); + $this->assertInstanceOf('Symfony\Component\Console\Exception\ExceptionInterface', $e, '->get() throws domain specific exception when helper not found'); + $this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws InvalidArgumentException when helper not found'); + } + } + + public function testSetCommand() + { + $cmd_01 = new Command('foo'); + $cmd_02 = new Command('bar'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $helperset->setCommand($cmd_02); + $this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls'); + } + + public function testGetCommand() + { + $cmd = new Command('foo'); + $helperset = new HelperSet(); + $helperset->setCommand($cmd); + $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command'); + } + + public function testIteration() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + + $helpers = array('fake_helper_01', 'fake_helper_02'); + $i = 0; + + foreach ($helperset as $helper) { + $this->assertEquals($helpers[$i++], $helper->getName()); + } + } + + /** + * Create a generic mock for the helper interface. Optionally check for a call to setHelperSet with a specific + * helperset instance. + * + * @param string $name + * @param HelperSet $helperset allows a mock to verify a particular helperset set is being added to the Helper + */ + private function getGenericMockHelper($name, HelperSet $helperset = null) + { + $mock_helper = $this->getMock('\Symfony\Component\Console\Helper\HelperInterface'); + $mock_helper->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + + if ($helperset) { + $mock_helper->expects($this->any()) + ->method('setHelperSet') + ->with($this->equalTo($helperset)); + } + + return $mock_helper; + } +} diff --git a/vendor/symfony/console/Tests/Helper/HelperTest.php b/vendor/symfony/console/Tests/Helper/HelperTest.php new file mode 100644 index 00000000000..33fa22051a0 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/HelperTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Helper; + +class HelperTest extends \PHPUnit_Framework_TestCase +{ + public function formatTimeProvider() + { + return array( + array(0, '< 1 sec'), + array(1, '1 sec'), + array(2, '2 secs'), + array(59, '59 secs'), + array(60, '1 min'), + array(61, '1 min'), + array(119, '1 min'), + array(120, '2 mins'), + array(121, '2 mins'), + array(3599, '59 mins'), + array(3600, '1 hr'), + array(7199, '1 hr'), + array(7200, '2 hrs'), + array(7201, '2 hrs'), + array(86399, '23 hrs'), + array(86400, '1 day'), + array(86401, '1 day'), + array(172799, '1 day'), + array(172800, '2 days'), + array(172801, '2 days'), + ); + } + + /** + * @dataProvider formatTimeProvider + * + * @param int $secs + * @param string $expectedFormat + */ + public function testFormatTime($secs, $expectedFormat) + { + $this->assertEquals($expectedFormat, Helper::formatTime($secs)); + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php new file mode 100644 index 00000000000..a51fb4359dc --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Process\Process; + +class ProcessHelperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideCommandsAndOutput + */ + public function testVariousProcessRuns($expected, $cmd, $verbosity, $error) + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream($verbosity); + $helper->run($output, $cmd, $error); + $this->assertEquals($expected, $this->getOutput($output)); + } + + public function testPassedCallbackIsExecuted() + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream(StreamOutput::VERBOSITY_NORMAL); + + $executed = false; + $callback = function () use (&$executed) { $executed = true; }; + + $helper->run($output, 'php -r "echo 42;"', null, $callback); + $this->assertTrue($executed); + } + + public function provideCommandsAndOutput() + { + $successOutputVerbose = <<42
';" + OUT 42 + RES Command ran successfully + +EOT; + $successOutputProcessDebug = <<42
\';"', StreamOutput::VERBOSITY_DEBUG, null), + array('', 'php -r "syntax error"', StreamOutput::VERBOSITY_VERBOSE, null), + array($syntaxErrorOutputVerbose, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), + array($syntaxErrorOutputDebug, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, null), + array($errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERBOSE, $errorMessage), + array($syntaxErrorOutputVerbose.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, $errorMessage), + array($syntaxErrorOutputDebug.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, $errorMessage), + array($successOutputProcessDebug, array('php', '-r', 'echo 42;'), StreamOutput::VERBOSITY_DEBUG, null), + array($successOutputDebug, new Process('php -r "echo 42;"'), StreamOutput::VERBOSITY_DEBUG, null), + ); + } + + private function getOutputStream($verbosity) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, false); + } + + private function getOutput(StreamOutput $output) + { + rewind($output->getStream()); + + return stream_get_contents($output->getStream()); + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressBarTest.php b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php new file mode 100644 index 00000000000..db2325b7304 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php @@ -0,0 +1,664 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group time-sensitive + */ +class ProgressBarTest extends \PHPUnit_Framework_TestCase +{ + public function testMultipleStart() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + $bar->start(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'. + $this->generateOutput(' 1 [->--------------------------]'). + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvance() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'. + $this->generateOutput(' 1 [->--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceWithStep() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(5); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'. + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceMultipleTimes() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(3); + $bar->advance(2); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'. + $this->generateOutput(' 3 [--->------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceOverMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setProgress(9); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 9/10 [=========================>--] 90%'. + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 11/11 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testFormat() + { + $expected = + ' 0/10 [>---------------------------] 0%'. + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 10/10 [============================] 100%') + ; + + // max in construct, no format + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, no format + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in construct, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setBarWidth(10); + $bar->setBarCharacter('_'); + $bar->setEmptyBarCharacter(' '); + $bar->setProgressCharacter('/'); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/10 [/ ] 0%'. + $this->generateOutput(' 1/10 [_/ ] 10%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%', + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithQuietVerbosity() + { + $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + '', + stream_get_contents($output->getStream()) + ); + } + + public function testFinishWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 50/50 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testPercent() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'. + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] 4%'), + stream_get_contents($output->getStream()) + ); + } + + public function testOverwriteWithShorterLine() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->display(); + $bar->advance(); + + // set shorter format + $bar->setFormat(' %current%/%max% [%bar%]'); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'. + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testStartWithMax() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('%current%/%max% [%bar%]'); + $bar->start(50); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------]'. + $this->generateOutput(' 1/50 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->setProgress(15); + $bar->setProgress(25); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'. + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 15/50 [========>-------------------] 30%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'), + stream_get_contents($output->getStream()) + ); + } + + /** + */ + public function testSetCurrentBeforeStarting() + { + $bar = new ProgressBar($this->getOutputStream()); + $bar->setProgress(15); + $this->assertNotNull($bar->getStartTime()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(15); + $bar->setProgress(10); + } + + public function testRedrawFrequency() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream(), 6)); + $bar->expects($this->exactly(4))->method('display'); + + $bar->setRedrawFrequency(2); + $bar->start(); + $bar->setProgress(1); + $bar->advance(2); + $bar->advance(2); + $bar->advance(1); + } + + public function testRedrawFrequencyIsAtLeastOneIfZeroGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0); + $bar->start(); + $bar->advance(); + } + + public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0.9); + $bar->start(); + $bar->advance(); + } + + public function testMultiByteSupport() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->setBarCharacter('■'); + $bar->advance(3); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'. + $this->generateOutput(' 3 [■■■>------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'. + $this->generateOutput(' 25/50 [==============>-------------] 50%'). + $this->generateOutput(''), + stream_get_contents($output->getStream()) + ); + } + + public function testPercentNotHundredBeforeComplete() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 200); + $bar->start(); + $bar->display(); + $bar->advance(199); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/200 [>---------------------------] 0%'. + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 199/200 [===========================>] 99%'). + $this->generateOutput(' 200/200 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 200); + $bar->start(); + + for ($i = 0; $i < 200; ++$i) { + $bar->advance(); + } + + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/200 [>---------------------------] 0%'.PHP_EOL. + ' 20/200 [==>-------------------------] 10%'.PHP_EOL. + ' 40/200 [=====>----------------------] 20%'.PHP_EOL. + ' 60/200 [========>-------------------] 30%'.PHP_EOL. + ' 80/200 [===========>----------------] 40%'.PHP_EOL. + ' 100/200 [==============>-------------] 50%'.PHP_EOL. + ' 120/200 [================>-----------] 60%'.PHP_EOL. + ' 140/200 [===================>--------] 70%'.PHP_EOL. + ' 160/200 [======================>-----] 80%'.PHP_EOL. + ' 180/200 [=========================>--] 90%'.PHP_EOL. + ' 200/200 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + $bar->setProgress(50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'.PHP_EOL. + ' 25/50 [==============>-------------] 50%'.PHP_EOL. + ' 50/50 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithoutMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(false)); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'.PHP_EOL. + ' 1 [->--------------------------]', + stream_get_contents($output->getStream()) + ); + } + + public function testParallelBars() + { + $output = $this->getOutputStream(); + $bar1 = new ProgressBar($output, 2); + $bar2 = new ProgressBar($output, 3); + $bar2->setProgressCharacter('#'); + $bar3 = new ProgressBar($output); + + $bar1->start(); + $output->write("\n"); + $bar2->start(); + $output->write("\n"); + $bar3->start(); + + for ($i = 1; $i <= 3; ++$i) { + // up two lines + $output->write("\033[2A"); + if ($i <= 2) { + $bar1->advance(); + } + $output->write("\n"); + $bar2->advance(); + $output->write("\n"); + $bar3->advance(); + } + $output->write("\033[2A"); + $output->write("\n"); + $output->write("\n"); + $bar3->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/2 [>---------------------------] 0%'."\n". + ' 0/3 [#---------------------------] 0%'."\n". + rtrim(' 0 [>---------------------------]'). + + "\033[2A". + $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n". + $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n". + rtrim($this->generateOutput(' 1 [->--------------------------]')). + + "\033[2A". + $this->generateOutput(' 2/2 [============================] 100%')."\n". + $this->generateOutput(' 2/3 [==================#---------] 66%')."\n". + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + + "\033[2A". + "\n". + $this->generateOutput(' 3/3 [============================] 100%')."\n". + rtrim($this->generateOutput(' 3 [--->------------------------]')). + + "\033[2A". + "\n". + "\n". + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testWithoutMax() + { + $output = $this->getOutputStream(); + + $bar = new ProgressBar($output); + $bar->start(); + $bar->advance(); + $bar->advance(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + rtrim(' 0 [>---------------------------]'). + rtrim($this->generateOutput(' 1 [->--------------------------]')). + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + rtrim($this->generateOutput(' 3 [--->------------------------]')). + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testAddingPlaceholderFormatter() + { + ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) { + return $bar->getMaxSteps() - $bar->getProgress(); + }); + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat(' %remaining_steps% [%bar%]'); + + $bar->start(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 3 [>---------------------------]'. + $this->generateOutput(' 2 [=========>------------------]'). + $this->generateOutput(' 0 [============================]'), + stream_get_contents($output->getStream()) + ); + } + + public function testMultilineFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat("%bar%\nfoobar"); + + $bar->start(); + $bar->advance(); + $bar->clear(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ">---------------------------\nfoobar". + $this->generateOutput("=========>------------------\nfoobar"). + "\x0D\x1B[2K\x1B[1A\x1B[2K". + $this->generateOutput("============================\nfoobar"), + stream_get_contents($output->getStream()) + ); + } + + public function testAnsiColorsAndEmojis() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 15); + ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) { + static $i = 0; + $mem = 100000 * $i; + $colors = $i++ ? '41;37' : '44;37'; + + return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m"; + }); + $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%"); + $bar->setBarCharacter($done = "\033[32m●\033[0m"); + $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m"); + $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m"); + + $bar->setMessage('Starting the demo... fingers crossed', 'title'); + $bar->start(); + $bar->setMessage('Looks good to me...', 'title'); + $bar->advance(4); + $bar->setMessage('Thanks, bye', 'title'); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + + " \033[44;37m Starting the demo... fingers crossed \033[0m\n". + ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m" + . + $this->generateOutput( + " \033[44;37m Looks good to me... \033[0m\n". + ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m" + ). + $this->generateOutput( + " \033[44;37m Thanks, bye \033[0m\n". + ' 15/15 '.str_repeat($done, 28)." 100%\n". + " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m" + ), + stream_get_contents($output->getStream()) + ); + } + + public function testSetFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]', + stream_get_contents($output->getStream()) + ); + + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + ' 0/10 [>---------------------------] 0%', + stream_get_contents($output->getStream()) + ); + } + + /** + * @dataProvider provideFormat + */ + public function testFormatsWithoutMax($format) + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat($format); + $bar->start(); + + rewind($output->getStream()); + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php new file mode 100644 index 00000000000..192625263db --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php @@ -0,0 +1,182 @@ +getOutputStream()); + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->setMessage('Advancing...'); + $bar->advance(); + $bar->finish('Done...'); + $bar->start('Starting Again...'); + usleep(101000); + $bar->advance(); + $bar->finish('Done Again...'); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' | Starting...'). + $this->generateOutput(' / Starting...'). + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' \\ Advancing...'). + $this->generateOutput(' | Advancing...'). + $this->generateOutput(' | Done... '). + PHP_EOL. + $this->generateOutput(' - Starting Again...'). + $this->generateOutput(' \\ Starting Again...'). + $this->generateOutput(' \\ Done Again... '). + PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(false)); + + $bar->start('Starting...'); + $bar->advance(); + $bar->advance(); + $bar->setMessage('Midway...'); + $bar->advance(); + $bar->advance(); + $bar->finish('Done...'); + + rewind($output->getStream()); + + $this->assertEquals( + ' Starting...'.PHP_EOL. + ' Midway... '.PHP_EOL. + ' Done... '.PHP_EOL.PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testCustomIndicatorValues() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, array('a', 'b', 'c')); + + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' b Starting...'). + $this->generateOutput(' c Starting...'). + $this->generateOutput(' a Starting...'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Must have at least 2 indicator value characters. + */ + public function testCannotSetInvalidIndicatorCharacters() + { + $bar = new ProgressIndicator($this->getOutputStream(), null, 100, array('1')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator already started. + */ + public function testCannotStartAlreadyStartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->start('Starting...'); + $bar->start('Starting Again.'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotAdvanceUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->advance(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotFinishUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->finish('Finished'); + } + + /** + * @dataProvider provideFormat + */ + public function testFormats($format) + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), $format); + $bar->start('Starting...'); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php new file mode 100644 index 00000000000..6a4f8aceae9 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php @@ -0,0 +1,435 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * @group tty + */ +class QuestionHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAskChoice() + { + $questionHelper = new QuestionHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $questionHelper->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); + $question->setMaxAttempts(1); + // first answer is an empty answer, we're supposed to receive the default value + $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setMaxAttempts(1); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setErrorMessage('Input "%s" is not a superhero!'); + $question->setMaxAttempts(2); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $stream = stream_get_contents($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', $stream); + + try { + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); + $question->setMaxAttempts(1); + $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAsk() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm + // AcsTest + // + // + // Test + // + // S + // F00oo + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new Question('Please select a bundle', 'FrameworkBundle'); + $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskWithAutocompleteWithNonSequentialKeys() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // + $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $dialog->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle')); + $question->setMaxAttempts(1); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskHiddenResponse() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $question = new Question('What time is it?'); + $question->setHidden(true); + + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + /** + * @dataProvider getAskConfirmationData + */ + public function testAskConfirmation($question, $expected, $default = true) + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream($question."\n")); + $question = new ConfirmationQuestion('Do you like French fries?', $default); + $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); + } + + public function getAskConfirmationData() + { + return array( + array('', true), + array('', false, false), + array('y', true), + array('yes', true), + array('n', false), + array('no', false), + ); + } + + public function testAskConfirmationWithCustomTrueAnswer() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("j\ny\n")); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskAndValidate() + { + $dialog = new QuestionHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new \InvalidArgumentException($error); + } + + return $color; + }; + + $question = new Question('What color was the white horse of Henry IV?', 'white'); + $question->setValidator($validator); + $question->setMaxAttempts(2); + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + } + } + + /** + * @dataProvider simpleAnswerProvider + */ + public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'My environment 1', + 'My environment 2', + 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function simpleAnswerProvider() + { + return array( + array(0, 'My environment 1'), + array(1, 'My environment 2'), + array(2, 'My environment 3'), + array('My environment 1', 'My environment 1'), + array('My environment 2', 'My environment 2'), + array('My environment 3', 'My environment 3'), + ); + } + + /** + * @dataProvider mixedKeysChoiceListAnswerProvider + */ + public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) + { + $possibleChoices = array( + '0' => 'No environment', + '1' => 'My environment 1', + 'env_2' => 'My environment 2', + 3 => 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function mixedKeysChoiceListAnswerProvider() + { + return array( + array('0', '0'), + array('No environment', '0'), + array('1', '1'), + array('env_2', 'env_2'), + array(3, '3'), + array('My environment 1', '1'), + ); + } + + /** + * @dataProvider answerProvider + */ + public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'env_1' => 'My environment 1', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. + */ + public function testAmbiguousChoiceFromChoicelist() + { + $possibleChoices = array( + 'env_1' => 'My first environment', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("My environment\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + } + + public function answerProvider() + { + return array( + array('env_1', 'env_1'), + array('env_2', 'env_2'), + array('env_3', 'env_3'), + array('My environment 1', 'env_1'), + ); + } + + public function testNoInteraction() + { + $dialog = new QuestionHelper(); + $question = new Question('Do you have a job?', 'not yet'); + $this->assertEquals('not yet', $dialog->ask($this->createInputInterfaceMock(false), $this->createOutputInterface(), $question)); + } + + /** + * @requires function mb_strwidth + */ + public function testChoiceOutputFormattingQuestionForUtf8Keys() + { + $question = 'Lorem ipsum?'; + $possibleChoices = array( + 'foo' => 'foo', + 'żółw' => 'bar', + 'łabądź' => 'baz', + ); + $outputShown = array( + $question, + ' [foo ] foo', + ' [żółw ] bar', + ' [łabądź] baz', + ); + $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface'); + $output->method('getFormatter')->willReturn(new OutputFormatter()); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); + + $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); + $dialog->ask($this->createInputInterfaceMock(), $output, $question); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function createOutputInterface() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected function createInputInterfaceMock($interactive = true) + { + $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $mock->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue($interactive)); + + return $mock; + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php new file mode 100644 index 00000000000..032e153f068 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php @@ -0,0 +1,109 @@ +setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); + $question->setMaxAttempts(1); + // first answer is an empty answer, we're supposed to receive the default value + $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + $this->assertOutputContains('What is your favorite superhero? [Spiderman]', $output); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setMaxAttempts(1); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setErrorMessage('Input "%s" is not a superhero!'); + $question->setMaxAttempts(2); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + $this->assertOutputContains('Input "Fabien" is not a superhero!', $output); + + try { + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); + $question->setMaxAttempts(1); + $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + $this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + $this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function createOutputInterface() + { + $output = new StreamOutput(fopen('php://memory', 'r+', false)); + $output->setDecorated(false); + + return $output; + } + + protected function createInputInterfaceMock($interactive = true) + { + $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $mock->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue($interactive)); + + return $mock; + } + + private function assertOutputContains($expected, StreamOutput $output) + { + rewind($output->getStream()); + $stream = stream_get_contents($output->getStream()); + $this->assertContains($expected, $stream); + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableStyleTest.php b/vendor/symfony/console/Tests/Helper/TableStyleTest.php new file mode 100644 index 00000000000..587d8414e6e --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableStyleTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableStyle; + +class TableStyleTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH). + */ + public function testSetPadTypeWithInvalidType() + { + $style = new TableStyle(); + $style->setPadType('TEST'); + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableTest.php b/vendor/symfony/console/Tests/Helper/TableTest.php new file mode 100644 index 00000000000..d1991c50ec5 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableTest.php @@ -0,0 +1,708 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableStyle; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Output\StreamOutput; + +class TableTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'r+'); + } + + protected function tearDown() + { + fclose($this->stream); + $this->stream = null; + } + + /** + * @dataProvider testRenderProvider + */ + public function testRender($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setStyle($style) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + $books = array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ); + + return array( + array( + array('ISBN', 'Title', 'Author'), + $books, + 'default', +<< array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-700', 'Divine Com', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
99921-58-10-700 | Divine Com | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++----------------------------------+----------------------+-----------------+ + +TABLE + ), + 'Cell with colspan' => array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + new TableSeparator(), + array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))), + new TableSeparator(), + array( + new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)), + 'Mark Schmidt', + ), + new TableSeparator(), + array( + '9971-5-0210-0', + new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)), + ), + new TableSeparator(), + array( + new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', array('colspan' => 3)), + ), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3)), + 'Divine Comedy', + 'Dante Alighieri', + ), + array('A Tale of Two Cities', 'Charles Dickens'), + array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"), + new TableSeparator(), + array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'), + array('80-902734-1-7', 'Test'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)), + ), + array('J. R. R. Tolkien'), + array('J. R. R'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + new TableSeparator(), + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)), + ), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + ), + 'default', +<<
array( + array('ISBN', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)), + 'Dante Alighieri', + ), + array(new TableSeparator()), + array('Charles Dickens'), + ), + 'default', +<<
array( + array( + array(new TableCell('Main title', array('colspan' => 3))), + array('ISBN', 'Title', 'Author'), + ), + array(), + 'default', +<<
array( + array(), + array( + array( + new TableCell('1', array('colspan' => 3)), + new TableCell('2', array('colspan' => 2)), + new TableCell('3', array('colspan' => 2)), + new TableCell('4', array('colspan' => 2)), + ), + ), + 'default', +<<
getOutputStream()); + $table + ->setHeaders(array('■■')) + ->setRows(array(array(1234))) + ->setStyle('default') + ; + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testStyle() + { + $style = new TableStyle(); + $style + ->setHorizontalBorderChar('.') + ->setVerticalBorderChar('.') + ->setCrossingChar('.') + ; + + Table::setStyleDefinition('dotfull', $style); + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array(array('Bar'))) + ->setStyle('dotfull'); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRowSeparator() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array( + array('Bar1'), + new TableSeparator(), + array('Bar2'), + new TableSeparator(), + array('Bar3'), + )); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + + $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works'); + } + + public function testRenderMultiCalls() + { + $table = new Table($output = $this->getOutputStream()); + $table->setRows(array( + array(new TableCell('foo', array('colspan' => 2))), + )); + $table->render(); + $table->render(); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnStyle() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), + )); + + $style = new TableStyle(); + $style->setPadType(STR_PAD_LEFT); + $table->setColumnStyle(3, $style); + + $table->render(); + + $expected = + <<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnWith() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), + )) + ->setColumnWidth(0, 15) + ->setColumnWidth(3, 10); + + $style = new TableStyle(); + $style->setPadType(STR_PAD_LEFT); + $table->setColumnStyle(3, $style); + + $table->render(); + + $expected = + <<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnWiths() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), + )) + ->setColumnWidths(array(15, 0, -1, 10)); + + $style = new TableStyle(); + $style->setPadType(STR_PAD_LEFT); + $table->setColumnStyle(3, $style); + + $table->render(); + + $expected = + <<
assertEquals($expected, $this->getOutputContent($output)); + } + + protected function getOutputStream() + { + return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); + } + + protected function getOutputContent(StreamOutput $output) + { + rewind($output->getStream()); + + return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArgvInputTest.php b/vendor/symfony/console/Tests/Input/ArgvInputTest.php new file mode 100644 index 00000000000..dd217ed8453 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArgvInputTest.php @@ -0,0 +1,336 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArgvInputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $_SERVER['argv'] = array('cli.php', 'foo'); + $input = new ArgvInput(); + $r = new \ReflectionObject($input); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + + $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable'); + } + + public function testParseArguments() + { + $input = new ArgvInput(array('cli.php', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArgvInput($input); + $input->bind(new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('cli.php', '--foo'), + array(new InputOption('foo')), + array('foo' => true), + '->parse() parses long options without a value', + ), + array( + array('cli.php', '--foo=bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a = separator)', + ), + array( + array('cli.php', '--foo', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f'), + array(new InputOption('foo', 'f')), + array('foo' => true), + '->parse() parses short options without a value', + ), + array( + array('cli.php', '-fbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with no separator)', + ), + array( + array('cli.php', '-f', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f', ''), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value', + ), + array( + array('cli.php', '-f', '', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value followed by an argument', + ), + array( + array('cli.php', '-f', '', '-b'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => '', 'bar' => true), + '->parse() parses short options with an optional empty value followed by an option', + ), + array( + array('cli.php', '-f', '-b', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => null, 'bar' => true), + '->parse() parses short options with an optional value which is not present', + ), + array( + array('cli.php', '-fb'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b')), + array('foo' => true, 'bar' => true), + '->parse() parses short options when they are aggregated as a single one', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has a required value', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => 'bbar', 'bar' => null), + '->parse() parses short options when they are aggregated as a single one and one of them takes a value', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testInvalidInput($argv, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('RuntimeException', $expectedExceptionMessage); + + $input = new ArgvInput($argv); + $input->bind($definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('cli.php', '--foo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-ffoo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "-o" option does not exist.', + ), + array( + array('cli.php', '--foo=bar'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "--foo" option does not accept a value.', + ), + array( + array('cli.php', 'foo', 'bar'), + new InputDefinition(), + 'Too many arguments.', + ), + array( + array('cli.php', '--foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(), + 'The "-f" option does not exist.', + ), + array( + array('cli.php', '-1'), + new InputDefinition(array(new InputArgument('number'))), + 'The "-1" option does not exist.', + ), + ); + } + + public function testParseArrayArgument() + { + $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments'); + } + + public function testParseArrayOption() + { + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption')); + $input->bind(new InputDefinition(array( + new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('anotherOption', null, InputOption::VALUE_NONE), + ))); + $this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)'); + } + + public function testParseNegativeNumberAfterDoubleDash() + { + $input = new ArgvInput(array('cli.php', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number')))); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + } + + public function testParseEmptyStringArgument() + { + $input = new ArgvInput(array('cli.php', '-f', 'bar', '')); + $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + + $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments'); + } + + public function testGetFirstArgument() + { + $input = new ArgvInput(array('cli.php', '-fbbar')); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null when there is no arguments'); + + $input = new ArgvInput(array('cli.php', '-fbbar', 'foo')); + $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + } + + public function testHasParameterOption() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', 'foo')); + $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given option with provided value is in the raw input'); + } + + public function testHasParameterOptionOnlyOptions() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f', true), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', '--', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option with provided value is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--', '--foo')); + $this->assertFalse($input->hasParameterOption('--foo', true), '->hasParameterOption() returns false if the given option is in the raw input but after an end of options signal'); + } + + public function testToString() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertEquals('-f foo', (string) $input); + + $input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C")); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } + + /** + * @dataProvider provideGetParameterOptionValues + */ + public function testGetParameterOptionEqualSign($argv, $key, $onlyParams, $expected) + { + $input = new ArgvInput($argv); + $this->assertEquals($expected, $input->getParameterOption($key, false, $onlyParams), '->getParameterOption() returns the expected value'); + } + + public function provideGetParameterOptionValues() + { + return array( + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', false, 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), false, '1'), + array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), false, '1'), + array(array('app/console', 'foo:bar', '--env', 'val'), '--env', false, 'val'), + array(array('app/console', 'foo:bar', '--env', 'val', '--dummy'), '--env', false, 'val'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', false, 'dev'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', true, false), + ); + } + + public function testParseSingleDashAsArgument() + { + $input = new ArgvInput(array('cli.php', '-')); + $input->bind(new InputDefinition(array(new InputArgument('file')))); + $this->assertEquals(array('file' => '-'), $input->getArguments(), '->parse() parses single dash as an argument'); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArrayInputTest.php b/vendor/symfony/console/Tests/Input/ArrayInputTest.php new file mode 100644 index 00000000000..afb9913a41f --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArrayInputTest.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArrayInputTest extends \PHPUnit_Framework_TestCase +{ + public function testGetFirstArgument() + { + $input = new ArrayInput(array()); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); + $input = new ArrayInput(array('name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + } + + public function testHasParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('--foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + + $input = new ArrayInput(array('--foo', '--', '--bar')); + $this->assertTrue($input->hasParameterOption('--bar'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar', true), '->hasParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); + } + + public function testGetParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + $this->assertFalse($input->getParameterOption('--bar'), '->getParameterOption() returns the default if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + + $input = new ArrayInput(array('--foo', '--', '--bar' => 'woop')); + $this->assertEquals('woop', $input->getParameterOption('--bar'), '->getParameterOption() returns the correct value if an option is present in the passed parameters'); + $this->assertFalse($input->getParameterOption('--bar', false, true), '->getParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); + } + + public function testParseArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArrayInput($input, new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('--foo' => 'bar'), + array(new InputOption('foo')), + array('foo' => 'bar'), + '->parse() parses long options', + ), + array( + array('--foo' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'bar'), + '->parse() parses long options with a default value', + ), + array( + array('--foo' => null), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() parses long options with a default value', + ), + array( + array('-f' => 'bar'), + array(new InputOption('foo', 'f')), + array('foo' => 'bar'), + '->parse() parses short options', + ), + array( + array('--' => null, '-f' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() does not parse opts after an end of options signal', + ), + array( + array('--' => null), + array(), + array(), + '->parse() does not choke on end of options signal', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testParseInvalidInput($parameters, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); + + new ArrayInput($parameters, $definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('foo' => 'foo'), + new InputDefinition(array(new InputArgument('name'))), + 'The "foo" argument does not exist.', + ), + array( + array('--foo' => null), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('--foo' => 'foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('-o' => 'foo'), + new InputDefinition(), + 'The "-o" option does not exist.', + ), + ); + } + + public function testToString() + { + $input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C")); + $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputArgumentTest.php b/vendor/symfony/console/Tests/Input/InputArgumentTest.php new file mode 100644 index 00000000000..cfb37cd4106 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputArgumentTest.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputArgument; + +class InputArgumentTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $argument = new InputArgument('foo'); + $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); + } + + public function testModes() + { + $argument = new InputArgument('foo'); + $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default'); + + $argument = new InputArgument('foo', null); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode)); + + new InputArgument('foo', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + public function testIsArray() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array'); + } + + public function testGetDescription() + { + $argument = new InputArgument('foo', null, 'Some description'); + $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); + } + + public function testGetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); + } + + public function testSetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $argument->setDefault(null); + $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); + $argument->setDefault('another'); + $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $argument->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value except for InputArgument::OPTIONAL mode. + */ + public function testSetDefaultWithRequiredArgument() + { + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $argument->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array argument must be an array. + */ + public function testSetDefaultWithArrayArgument() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $argument->setDefault('default'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputDefinitionTest.php b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php new file mode 100644 index 00000000000..739e107dea3 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php @@ -0,0 +1,405 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputDefinitionTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixtures; + + protected $foo; + protected $bar; + protected $foo1; + protected $foo2; + + public static function setUpBeforeClass() + { + self::$fixtures = __DIR__.'/../Fixtures/'; + } + + public function testConstructorArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); + } + + public function testConstructorOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); + } + + public function testSetArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->setArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); + $definition->setArguments(array($this->bar)); + + $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects'); + } + + public function testAddArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); + $definition->addArguments(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); + } + + public function testAddArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $definition->addArgument($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An argument with name "foo" already exists. + */ + public function testArgumentsMustHaveDifferentNames() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo1); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add an argument after an array argument. + */ + public function testArrayArgumentHasToBeLast() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY)); + $definition->addArgument(new InputArgument('anotherbar')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add a required argument after an optional one. + */ + public function testRequiredArgumentCannotFollowAnOptionalOne() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo2); + } + + public function testGetArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "bar" argument does not exist. + */ + public function testGetInvalidArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $definition->getArgument('bar'); + } + + public function testHasArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + + $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name'); + $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name'); + } + + public function testGetArgumentRequiredCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + } + + public function testGetArgumentCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + } + + public function testGetArgumentDefaults() + { + $definition = new InputDefinition(array( + new InputArgument('foo1', InputArgument::OPTIONAL), + new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), + new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), + // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + + $definition = new InputDefinition(array( + new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + } + + public function testSetOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); + $definition->setOptions(array($this->bar)); + $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-f" option does not exist. + */ + public function testSetOptionsClearsOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->setOptions(array($this->bar)); + $definition->getOptionForShortcut('f'); + } + + public function testAddOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); + $definition->addOptions(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); + } + + public function testAddOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object'); + $definition->addOption($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option named "foo" already exists. + */ + public function testAddDuplicateOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo2); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "f" already exists. + */ + public function testAddDuplicateShortcutOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo1); + } + + public function testGetOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testGetInvalidOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOption('bar'); + } + + public function testHasOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name'); + $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name'); + } + + public function testHasShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut'); + $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut'); + } + + public function testGetOptionForShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + public function testGetOptionForMultiShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->multi)); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-l" option does not exist. + */ + public function testGetOptionForInvalidShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOptionForShortcut('l'); + } + + public function testGetOptionDefaults() + { + $definition = new InputDefinition(array( + new InputOption('foo1', null, InputOption::VALUE_NONE), + new InputOption('foo2', null, InputOption::VALUE_REQUIRED), + new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'), + new InputOption('foo4', null, InputOption::VALUE_OPTIONAL), + new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'), + new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)), + )); + $defaults = array( + 'foo1' => false, + 'foo2' => null, + 'foo3' => 'default', + 'foo4' => null, + 'foo5' => 'default', + 'foo6' => array(), + 'foo7' => array(1, 2), + ); + $this->assertSame($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); + } + + /** + * @dataProvider getGetSynopsisData + */ + public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $message = null) + { + $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); + } + + public function getGetSynopsisData() + { + return array( + array(new InputDefinition(array(new InputOption('foo'))), '[--foo]', 'puts optional options in square brackets'), + array(new InputDefinition(array(new InputOption('foo', 'f'))), '[-f|--foo]', 'separates shortcut with a pipe'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), '[-f|--foo FOO]', 'uses shortcut as value placeholder'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))), '[-f|--foo [FOO]]', 'puts optional values in square brackets'), + + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))), '', 'puts arguments in angle brackets'), + array(new InputDefinition(array(new InputArgument('foo'))), '[]', 'puts optional arguments in square brackets'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))), '[]...', 'uses an ellipsis for array arguments'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))), ' ()...', 'uses parenthesis and ellipsis for required array arguments'), + + array(new InputDefinition(array(new InputOption('foo'), new InputArgument('foo', InputArgument::REQUIRED))), '[--foo] [--] ', 'puts [--] between options and arguments'), + ); + } + + public function testGetShortSynopsis() + { + $definition = new InputDefinition(array(new InputOption('foo'), new InputOption('bar'), new InputArgument('cat'))); + $this->assertEquals('[options] [--] []', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); + } + + protected function initializeArguments() + { + $this->foo = new InputArgument('foo'); + $this->bar = new InputArgument('bar'); + $this->foo1 = new InputArgument('foo'); + $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED); + } + + protected function initializeOptions() + { + $this->foo = new InputOption('foo', 'f'); + $this->bar = new InputOption('bar', 'b'); + $this->foo1 = new InputOption('fooBis', 'f'); + $this->foo2 = new InputOption('foo', 'p'); + $this->multi = new InputOption('multi', 'm|mm|mmm'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputOptionTest.php b/vendor/symfony/console/Tests/Input/InputOptionTest.php new file mode 100644 index 00000000000..53ce1df8cf3 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputOptionTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputOption; + +class InputOptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $option = new InputOption('foo'); + $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); + $option = new InputOption('--foo'); + $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value. + */ + public function testArrayModeWithoutValue() + { + new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY); + } + + public function testShortcut() + { + $option = new InputOption('foo', 'f'); + $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); + $option = new InputOption('foo', '-f|-ff|fff'); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo', array('f', 'ff', '-fff')); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo'); + $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); + } + + public function testModes() + { + $option = new InputOption('foo', 'f'); + $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + + $option = new InputOption('foo', 'f', null); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode)); + + new InputOption('foo', 'f', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEmptyNameIsInvalid() + { + new InputOption(''); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDoubleDashNameIsInvalid() + { + new InputOption('--'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSingleDashOptionIsInvalid() + { + new InputOption('foo', '-'); + } + + public function testIsArray() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array'); + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array'); + } + + public function testGetDescription() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); + } + + public function testGetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); + $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); + + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array'); + + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); + } + + public function testSetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $option->setDefault(null); + $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); + $option->setDefault('another'); + $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); + $option->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value when using InputOption::VALUE_NONE mode. + */ + public function testDefaultValueWithValueNoneMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $option->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array option must be an array. + */ + public function testDefaultValueWithIsArrayMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $option->setDefault('default'); + } + + public function testEquals() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', null, 'Alternative description'); + $this->assertTrue($option->equals($option2)); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('bar', 'f', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', '', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $this->assertFalse($option->equals($option2)); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputTest.php b/vendor/symfony/console/Tests/Input/InputTest.php new file mode 100644 index 00000000000..eb1c6617f56 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); + } + + public function testOptions() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name')))); + $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); + + $input->setOption('name', 'bar'); + $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); + $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values'); + + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testSetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->setOption('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testGetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->getOption('foo'); + } + + public function testArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); + + $input->setArgument('name', 'bar'); + $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); + $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values'); + + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testSetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->setArgument('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testGetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->getArgument('foo'); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingArguments() + { + $input = new ArrayInput(array()); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + $input->validate(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingRequiredArguments() + { + $input = new ArrayInput(array('bar' => 'baz')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED), new InputArgument('bar', InputArgument::OPTIONAL)))); + $input->validate(); + } + + public function testValidate() + { + $input = new ArrayInput(array('name' => 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + + $this->assertNull($input->validate()); + } + + public function testSetGetInteractive() + { + $input = new ArrayInput(array()); + $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not'); + $input->setInteractive(false); + $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); + } +} diff --git a/vendor/symfony/console/Tests/Input/StringInputTest.php b/vendor/symfony/console/Tests/Input/StringInputTest.php new file mode 100644 index 00000000000..7059cf05d5a --- /dev/null +++ b/vendor/symfony/console/Tests/Input/StringInputTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StringInput; + +class StringInputTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTokenizeData + */ + public function testTokenize($input, $tokens, $message) + { + $input = new StringInput($input); + $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput'); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + $this->assertEquals($tokens, $p->getValue($input), $message); + } + + public function testInputOptionWithGivenString() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + // call to bind + $input = new StringInput('--foo=bar'); + $input->bind($definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + public function getTokenizeData() + { + return array( + array('', array(), '->tokenize() parses an empty string'), + array('foo', array('foo'), '->tokenize() parses arguments'), + array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'), + array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'), + array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'), + array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'), + array("'a'\r'b'\n'c'\t'd'", array('a', 'b', 'c', 'd'), '->tokenize() parses whitespace chars between args as spaces'), + array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'), + array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'), + array('-a', array('-a'), '->tokenize() parses short options'), + array('-azc', array('-azc'), '->tokenize() parses aggregated short options'), + array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'), + array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('--long-option', array('--long-option'), '->tokenize() parses long options'), + array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar''another'", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar'\"another\"", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'), + ); + } + + public function testToString() + { + $input = new StringInput('-f foo'); + $this->assertEquals('-f foo', (string) $input); + + $input = new StringInput('-f --bar=foo "a b c d"'); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); + + $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'"); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php new file mode 100644 index 00000000000..c5eca2cafdc --- /dev/null +++ b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.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 Symfony\Component\Console\Tests\Logger; + +use Psr\Log\Test\LoggerInterfaceTest; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Logger\ConsoleLogger; +use Symfony\Component\Console\Tests\Fixtures\DummyOutput; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console logger test. + * + * @author Kévin Dunglas + */ +class ConsoleLoggerTest extends LoggerInterfaceTest +{ + /** + * @var DummyOutput + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function getLogger() + { + $this->output = new DummyOutput(OutputInterface::VERBOSITY_VERBOSE); + + return new ConsoleLogger($this->output, array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, + LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, + LogLevel::DEBUG => OutputInterface::VERBOSITY_NORMAL, + )); + } + + /** + * {@inheritdoc} + */ + public function getLogs() + { + return $this->output->getLogs(); + } +} diff --git a/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php new file mode 100644 index 00000000000..1afbbb6e6cf --- /dev/null +++ b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\Output; + +class ConsoleOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument'); + } +} diff --git a/vendor/symfony/console/Tests/Output/NullOutputTest.php b/vendor/symfony/console/Tests/Output/NullOutputTest.php new file mode 100644 index 00000000000..b20ae4e8d07 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/NullOutputTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; + +class NullOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new NullOutput(); + + ob_start(); + $output->write('foo'); + $buffer = ob_get_clean(); + + $this->assertSame('', $buffer, '->write() does nothing (at least nothing is printed)'); + $this->assertFalse($output->isDecorated(), '->isDecorated() returns false'); + } + + public function testVerbosity() + { + $output = new NullOutput(); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default'); + + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); + } +} diff --git a/vendor/symfony/console/Tests/Output/OutputTest.php b/vendor/symfony/console/Tests/Output/OutputTest.php new file mode 100644 index 00000000000..45e6ddc7dc3 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/OutputTest.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new TestOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + public function testSetIsDecorated() + { + $output = new TestOutput(); + $output->setDecorated(true); + $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag'); + } + + public function testSetGetVerbosity() + { + $output = new TestOutput(); + $output->setVerbosity(Output::VERBOSITY_QUIET); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity'); + + $this->assertTrue($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_NORMAL); + $this->assertFalse($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_DEBUG); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertTrue($output->isDebug()); + } + + public function testWriteWithVerbosityQuiet() + { + $output = new TestOutput(Output::VERBOSITY_QUIET); + $output->writeln('foo'); + $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET'); + } + + public function testWriteAnArrayOfMessages() + { + $output = new TestOutput(); + $output->writeln(array('foo', 'bar')); + $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output'); + } + + /** + * @dataProvider provideWriteArguments + */ + public function testWriteRawMessage($message, $type, $expectedOutput) + { + $output = new TestOutput(); + $output->writeln($message, $type); + $this->assertEquals($expectedOutput, $output->output); + } + + public function provideWriteArguments() + { + return array( + array('foo', Output::OUTPUT_RAW, "foo\n"), + array('foo', Output::OUTPUT_PLAIN, "foo\n"), + ); + } + + public function testWriteWithDecorationTurnedOff() + { + $output = new TestOutput(); + $output->setDecorated(false); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false'); + } + + public function testWriteDecoratedMessage() + { + $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink')); + $output = new TestOutput(); + $output->getFormatter()->setStyle('FOO', $fooStyle); + $output->setDecorated(true); + $output->writeln('foo'); + $this->assertEquals("\033[33;41;5mfoo\033[39;49;25m\n", $output->output, '->writeln() decorates the output'); + } + + public function testWriteWithInvalidStyle() + { + $output = new TestOutput(); + + $output->clear(); + $output->write('foo'); + $this->assertEquals('foo', $output->output, '->write() do nothing when a style does not exist'); + + $output->clear(); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() do nothing when a style does not exist'); + } + + /** + * @dataProvider verbosityProvider + */ + public function testWriteWithVerbosityOption($verbosity, $expected, $msg) + { + $output = new TestOutput(); + + $output->setVerbosity($verbosity); + $output->clear(); + $output->write('1', false); + $output->write('2', false, Output::VERBOSITY_QUIET); + $output->write('3', false, Output::VERBOSITY_NORMAL); + $output->write('4', false, Output::VERBOSITY_VERBOSE); + $output->write('5', false, Output::VERBOSITY_VERY_VERBOSE); + $output->write('6', false, Output::VERBOSITY_DEBUG); + $this->assertEquals($expected, $output->output, $msg); + } + + public function verbosityProvider() + { + return array( + array(Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'), + array(Output::VERBOSITY_NORMAL, '123', '->write() in NORMAL mode outputs anything below an explicit VERBOSE verbosity'), + array(Output::VERBOSITY_VERBOSE, '1234', '->write() in VERBOSE mode outputs anything below an explicit VERY_VERBOSE verbosity'), + array(Output::VERBOSITY_VERY_VERBOSE, '12345', '->write() in VERY_VERBOSE mode outputs anything below an explicit DEBUG verbosity'), + array(Output::VERBOSITY_DEBUG, '123456', '->write() in DEBUG mode outputs everything'), + ); + } +} + +class TestOutput extends Output +{ + public $output = ''; + + public function clear() + { + $this->output = ''; + } + + protected function doWrite($message, $newline) + { + $this->output .= $message.($newline ? "\n" : ''); + } +} diff --git a/vendor/symfony/console/Tests/Output/StreamOutputTest.php b/vendor/symfony/console/Tests/Output/StreamOutputTest.php new file mode 100644 index 00000000000..2fd4f612142 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/StreamOutputTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\StreamOutput; + +class StreamOutputTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'a', false); + } + + protected function tearDown() + { + $this->stream = null; + } + + public function testConstructor() + { + $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The StreamOutput class needs a stream as its first argument. + */ + public function testStreamIsRequired() + { + new StreamOutput('foo'); + } + + public function testGetStream() + { + $output = new StreamOutput($this->stream); + $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream'); + } + + public function testDoWrite() + { + $output = new StreamOutput($this->stream); + $output->writeln('foo'); + rewind($output->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream'); + } +} diff --git a/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php new file mode 100644 index 00000000000..e4ce037bb8f --- /dev/null +++ b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Style; + +use PHPUnit_Framework_TestCase; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Tester\CommandTester; + +class SymfonyStyleTest extends PHPUnit_Framework_TestCase +{ + /** @var Command */ + protected $command; + /** @var CommandTester */ + protected $tester; + + protected function setUp() + { + $this->command = new Command('sfstyle'); + $this->tester = new CommandTester($this->command); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + /** + * @dataProvider inputCommandToOutputFilesProvider + */ + public function testOutputs($inputCommandFilepath, $outputFilepath) + { + $code = require $inputCommandFilepath; + $this->command->setCode($code); + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); + } + + public function inputCommandToOutputFilesProvider() + { + $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; + + return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt')); + } +} + +/** + * Use this class in tests to force the line length + * and ensure a consistent output for expectations. + */ +class SymfonyStyleWithForcedLineLength extends SymfonyStyle +{ + public function __construct(InputInterface $input, OutputInterface $output) + { + parent::__construct($input, $output); + + $ref = new \ReflectionProperty(get_parent_class($this), 'lineLength'); + $ref->setAccessible(true); + $ref->setValue($this, 120); + } +} diff --git a/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php new file mode 100644 index 00000000000..a8389dd1866 --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $application; + protected $tester; + + protected function setUp() + { + $this->application = new Application(); + $this->application->setAutoExit(false); + $this->application->register('foo') + ->addArgument('foo') + ->setCode(function ($input, $output) { $output->writeln('foo'); }) + ; + + $this->tester = new ApplicationTester($this->application); + $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->application = null; + $this->tester = null; + } + + public function testRun() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } +} diff --git a/vendor/symfony/console/Tests/Tester/CommandTesterTest.php b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php new file mode 100644 index 00000000000..b54c00e83dc --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $command; + protected $tester; + + protected function setUp() + { + $this->command = new Command('foo'); + $this->command->addArgument('command'); + $this->command->addArgument('foo'); + $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $this->tester = new CommandTester($this->command); + $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + public function testExecute() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } + + public function testCommandFromApplication() + { + $application = new Application(); + $application->setAutoExit(false); + + $command = new Command('foo'); + $command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $application->add($command); + + $tester = new CommandTester($application->find('foo')); + + // check that there is no need to pass the command name here + $this->assertEquals(0, $tester->execute(array())); + } +} diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json new file mode 100644 index 00000000000..a013837dd6e --- /dev/null +++ b/vendor/symfony/console/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/event-dispatcher": "", + "symfony/process": "", + "psr/log": "For using the console logger" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Console\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + } +} diff --git a/vendor/symfony/console/phpunit.xml.dist b/vendor/symfony/console/phpunit.xml.dist new file mode 100644 index 00000000000..8c09554f80f --- /dev/null +++ b/vendor/symfony/console/phpunit.xml.dist @@ -0,0 +1,39 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + + + + + + Symfony\Component\Console + + + + + diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 00000000000..39fa189d2b5 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2016 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/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 00000000000..85a0668172d --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,604 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Mbstring; + +/** + * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. + * + * Implemented: + * - mb_convert_encoding - Convert character encoding + * - mb_convert_variables - Convert character code in variable(s) + * - mb_decode_mimeheader - Decode string in MIME header field + * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_convert_case - Perform case folding on a string + * - mb_get_info - Get internal settings of mbstring + * - mb_http_input - Detect HTTP input character encoding + * - mb_http_output - Set/Get HTTP output character encoding + * - mb_internal_encoding - Set/Get internal character encoding + * - mb_list_encodings - Returns an array of all supported encodings + * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_strlen - Get string length + * - mb_strpos - Find position of first occurrence of string in a string + * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_strtolower - Make a string lowercase + * - mb_strtoupper - Make a string uppercase + * - mb_substitute_character - Set/Get substitution character + * - mb_substr - Get part of string + * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive + * - mb_stristr - Finds first occurrence of a string within another, case insensitive + * - mb_strrchr - Finds the last occurrence of a character in a string within another + * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive + * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive + * - mb_strstr - Finds first occurrence of a string within anothers + * - mb_strwidth - Return width of string + * - mb_substr_count - Count the number of substring occurrences + * + * Not implemented: + * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_ereg_* - Regular expression with multibyte support + * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable + * - mb_preferred_mime_name - Get MIME charset string + * - mb_regex_encoding - Returns current encoding for multibyte regex as string + * - mb_regex_set_options - Set/Get the default options for mbregex functions + * - mb_send_mail - Send encoded mail + * - mb_split - Split multibyte string using regular expression + * - mb_strcut - Get part of string + * - mb_strimwidth - Get truncated string with specified width + * + * @author Nicolas Grekas + * + * @internal + */ +final class Mbstring +{ + const MB_CASE_FOLD = PHP_INT_MAX; + + private static $encodingList = array('ASCII', 'UTF-8'); + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + private static $caseFold = array( + array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"), + array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'), + ); + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); + } else { + $fromEncoding = self::getEncoding($fromEncoding); + } + + $toEncoding = self::getEncoding($toEncoding); + + if ('BASE64' === $fromEncoding) { + $s = base64_decode($s); + $fromEncoding = $toEncoding; + } + + if ('BASE64' === $toEncoding) { + return base64_encode($s); + } + + if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { + if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { + $fromEncoding = 'Windows-1252'; + } + if ('UTF-8' !== $fromEncoding) { + $s = iconv($fromEncoding, 'UTF-8', $s); + } + + return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + } + + if ('HTML-ENTITIES' === $fromEncoding) { + $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $fromEncoding = 'UTF-8'; + } + + return iconv($fromEncoding, $toEncoding, $s); + } + + public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + { + $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); + + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + $ok = false; + } + }); + + return $ok ? $fromEncoding : false; + } + + public static function mb_decode_mimeheader($s) + { + return iconv_mime_decode($s, 2, self::$internalEncoding); + } + + public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) + { + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + if ('' === $s .= '') { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + } else { + $s = iconv($encoding, 'UTF-8', $s); + } + + if (MB_CASE_TITLE == $mode) { + $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s); + $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s); + } else { + if (MB_CASE_UPPER == $mode) { + static $upper = null; + if (null === $upper) { + $upper = self::getData('upperCase'); + } + $map = $upper; + } else { + if (self::MB_CASE_FOLD === $mode) { + $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $i = 0; + $len = strlen($s); + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if (isset($map[$uchr])) { + $uchr = $map[$uchr]; + $nlen = strlen($uchr); + + if ($nlen == $ulen) { + $nlen = $i; + do { + $s[--$nlen] = $uchr[--$ulen]; + } while ($ulen); + } else { + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); + $len += $nlen - $ulen; + $i += $nlen - $ulen; + } + } + } + } + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding, $s); + } + + public static function mb_internal_encoding($encoding = null) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { + self::$internalEncoding = $encoding; + + return true; + } + + return false; + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($lang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $lang; + + return true; + } + + return false; + } + + public static function mb_list_encodings() + { + return array('UTF-8'); + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return array('utf8'); + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + } + + public static function mb_detect_encoding($str, $encodingList = null, $strict = false) + { + if (null === $encodingList) { + $encodingList = self::$encodingList; + } else { + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + } + + foreach ($encodingList as $enc) { + switch ($enc) { + case 'ASCII': + if (!preg_match('/[\x80-\xFF]/', $str)) { + return $enc; + } + break; + + case 'UTF8': + case 'UTF-8': + if (preg_match('//u', $str)) { + return 'UTF-8'; + } + break; + + default: + if (0 === strncmp($enc, 'ISO-8859-', 9)) { + return $enc; + } + } + } + + return false; + } + + public static function mb_detect_order($encodingList = null) + { + if (null === $encodingList) { + return self::$encodingList; + } + + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + + foreach ($encodingList as $enc) { + switch ($enc) { + default: + if (strncmp($enc, 'ISO-8859-', 9)) { + return false; + } + case 'ASCII': + case 'UTF8': + case 'UTF-8': + } + } + + self::$encodingList = $encodingList; + + return true; + } + + public static function mb_strlen($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + return iconv_strlen($s, $encoding); + } + + public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('' === $needle .= '') { + trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + + return false; + } + + return iconv_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ($offset != (int) $offset) { + $offset = 0; + } elseif ($offset = (int) $offset) { + if ($offset < 0) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_strtolower($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + } + + public static function mb_strtoupper($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + } + + public static function mb_substitute_character($c = null) + { + if (0 === strcasecmp($c, 'none')) { + return true; + } + + return null !== $c ? false : 'none'; + } + + public static function mb_substr($s, $start, $length = null, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ($start < 0) { + $start = iconv_strlen($s, $encoding) + $start; + if ($start < 0) { + $start = 0; + } + } + + if (null === $length) { + $length = 2147483647; + } elseif ($length < 0) { + $length = iconv_strlen($s, $encoding) + $length - $start; + if ($length < 0) { + return ''; + } + } + + return iconv_substr($s, $start, $length, $encoding).''; + } + + public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) + { + $pos = self::mb_stripos($haystack, $needle, 0, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) + { + $encoding = self::getEncoding($encoding); + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) + { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = self::mb_strripos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strrpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) + { + $pos = strpos($haystack, $needle); + if (false === $pos) { + return false; + } + if ($part) { + return substr($haystack, 0, $pos); + } + + return substr($haystack, $pos); + } + + public static function mb_get_info($type = 'all') + { + $info = array( + 'internal_encoding' => self::$internalEncoding, + 'http_output' => 'pass', + 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', + 'func_overload' => 0, + 'func_overload_list' => 'no overload', + 'mail_charset' => 'UTF-8', + 'mail_header_encoding' => 'BASE64', + 'mail_body_encoding' => 'BASE64', + 'illegal_chars' => 0, + 'encoding_translation' => 'Off', + 'language' => self::$language, + 'detect_order' => self::$encodingList, + 'substitute_character' => 'none', + 'strict_detection' => 'Off', + ); + + if ('all' === $type) { + return $info; + } + if (isset($info[$type])) { + return $info[$type]; + } + + return false; + } + + public static function mb_http_input($type = '') + { + return false; + } + + public static function mb_http_output($encoding = null) + { + return null !== $encoding ? 'pass' === $encoding : 'pass'; + } + + public static function mb_strwidth($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('UTF-8' !== $encoding) { + $s = iconv($encoding, 'UTF-8', $s); + } + + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + } + + public static function mb_substr_count($haystack, $needle, $encoding = null) + { + return substr_count($haystack, $needle); + } + + public static function mb_output_handler($contents, $status) + { + return $contents; + } + + private static function getSubpart($pos, $part, $haystack, $encoding) + { + if (false === $pos) { + return false; + } + if ($part) { + return self::mb_substr($haystack, 0, $pos, $encoding); + } + + return self::mb_substr($haystack, $pos, null, $encoding); + } + + private static function html_encoding_callback($m) + { + $i = 1; + $entities = ''; + $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + + while (isset($m[$i])) { + if (0x80 > $m[$i]) { + $entities .= chr($m[$i++]); + continue; + } + if (0xF0 <= $m[$i]) { + $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } elseif (0xE0 <= $m[$i]) { + $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } else { + $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; + } + + $entities .= '&#'.$c.';'; + } + + return $entities; + } + + private static function title_case_lower($s) + { + return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); + } + + private static function title_case_upper($s) + { + return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } + + private static function getEncoding($encoding) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = strtoupper($encoding); + + if ('8BIT' === $encoding || 'BINARY' === $encoding) { + return 'CP850'; + } + if ('UTF8' === $encoding) { + return 'UTF-8'; + } + + return $encoding; + } +} diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 00000000000..342e8286dbf --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Mbstring +=========================== + +This component provides a partial, native PHP implementation for the +[Mbstring](http://php.net/mbstring) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 00000000000..3ca16416a82 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -0,0 +1,1101 @@ + 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + 'À' => 'à', + 'Á' => 'á', + 'Â' => 'â', + 'Ã' => 'ã', + 'Ä' => 'ä', + 'Å' => 'å', + 'Æ' => 'æ', + 'Ç' => 'ç', + 'È' => 'è', + 'É' => 'é', + 'Ê' => 'ê', + 'Ë' => 'ë', + 'Ì' => 'ì', + 'Í' => 'í', + 'Î' => 'î', + 'Ï' => 'ï', + 'Ð' => 'ð', + 'Ñ' => 'ñ', + 'Ò' => 'ò', + 'Ó' => 'ó', + 'Ô' => 'ô', + 'Õ' => 'õ', + 'Ö' => 'ö', + 'Ø' => 'ø', + 'Ù' => 'ù', + 'Ú' => 'ú', + 'Û' => 'û', + 'Ü' => 'ü', + 'Ý' => 'ý', + 'Þ' => 'þ', + 'Ā' => 'ā', + 'Ă' => 'ă', + 'Ą' => 'ą', + 'Ć' => 'ć', + 'Ĉ' => 'ĉ', + 'Ċ' => 'ċ', + 'Č' => 'č', + 'Ď' => 'ď', + 'Đ' => 'đ', + 'Ē' => 'ē', + 'Ĕ' => 'ĕ', + 'Ė' => 'ė', + 'Ę' => 'ę', + 'Ě' => 'ě', + 'Ĝ' => 'ĝ', + 'Ğ' => 'ğ', + 'Ġ' => 'ġ', + 'Ģ' => 'ģ', + 'Ĥ' => 'ĥ', + 'Ħ' => 'ħ', + 'Ĩ' => 'ĩ', + 'Ī' => 'ī', + 'Ĭ' => 'ĭ', + 'Į' => 'į', + 'İ' => 'i', + 'IJ' => 'ij', + 'Ĵ' => 'ĵ', + 'Ķ' => 'ķ', + 'Ĺ' => 'ĺ', + 'Ļ' => 'ļ', + 'Ľ' => 'ľ', + 'Ŀ' => 'ŀ', + 'Ł' => 'ł', + 'Ń' => 'ń', + 'Ņ' => 'ņ', + 'Ň' => 'ň', + 'Ŋ' => 'ŋ', + 'Ō' => 'ō', + 'Ŏ' => 'ŏ', + 'Ő' => 'ő', + 'Œ' => 'œ', + 'Ŕ' => 'ŕ', + 'Ŗ' => 'ŗ', + 'Ř' => 'ř', + 'Ś' => 'ś', + 'Ŝ' => 'ŝ', + 'Ş' => 'ş', + 'Š' => 'š', + 'Ţ' => 'ţ', + 'Ť' => 'ť', + 'Ŧ' => 'ŧ', + 'Ũ' => 'ũ', + 'Ū' => 'ū', + 'Ŭ' => 'ŭ', + 'Ů' => 'ů', + 'Ű' => 'ű', + 'Ų' => 'ų', + 'Ŵ' => 'ŵ', + 'Ŷ' => 'ŷ', + 'Ÿ' => 'ÿ', + 'Ź' => 'ź', + 'Ż' => 'ż', + 'Ž' => 'ž', + 'Ɓ' => 'ɓ', + 'Ƃ' => 'ƃ', + 'Ƅ' => 'ƅ', + 'Ɔ' => 'ɔ', + 'Ƈ' => 'ƈ', + 'Ɖ' => 'ɖ', + 'Ɗ' => 'ɗ', + 'Ƌ' => 'ƌ', + 'Ǝ' => 'ǝ', + 'Ə' => 'ə', + 'Ɛ' => 'ɛ', + 'Ƒ' => 'ƒ', + 'Ɠ' => 'ɠ', + 'Ɣ' => 'ɣ', + 'Ɩ' => 'ɩ', + 'Ɨ' => 'ɨ', + 'Ƙ' => 'ƙ', + 'Ɯ' => 'ɯ', + 'Ɲ' => 'ɲ', + 'Ɵ' => 'ɵ', + 'Ơ' => 'ơ', + 'Ƣ' => 'ƣ', + 'Ƥ' => 'ƥ', + 'Ʀ' => 'ʀ', + 'Ƨ' => 'ƨ', + 'Ʃ' => 'ʃ', + 'Ƭ' => 'ƭ', + 'Ʈ' => 'ʈ', + 'Ư' => 'ư', + 'Ʊ' => 'ʊ', + 'Ʋ' => 'ʋ', + 'Ƴ' => 'ƴ', + 'Ƶ' => 'ƶ', + 'Ʒ' => 'ʒ', + 'Ƹ' => 'ƹ', + 'Ƽ' => 'ƽ', + 'DŽ' => 'dž', + 'Dž' => 'dž', + 'LJ' => 'lj', + 'Lj' => 'lj', + 'NJ' => 'nj', + 'Nj' => 'nj', + 'Ǎ' => 'ǎ', + 'Ǐ' => 'ǐ', + 'Ǒ' => 'ǒ', + 'Ǔ' => 'ǔ', + 'Ǖ' => 'ǖ', + 'Ǘ' => 'ǘ', + 'Ǚ' => 'ǚ', + 'Ǜ' => 'ǜ', + 'Ǟ' => 'ǟ', + 'Ǡ' => 'ǡ', + 'Ǣ' => 'ǣ', + 'Ǥ' => 'ǥ', + 'Ǧ' => 'ǧ', + 'Ǩ' => 'ǩ', + 'Ǫ' => 'ǫ', + 'Ǭ' => 'ǭ', + 'Ǯ' => 'ǯ', + 'DZ' => 'dz', + 'Dz' => 'dz', + 'Ǵ' => 'ǵ', + 'Ƕ' => 'ƕ', + 'Ƿ' => 'ƿ', + 'Ǹ' => 'ǹ', + 'Ǻ' => 'ǻ', + 'Ǽ' => 'ǽ', + 'Ǿ' => 'ǿ', + 'Ȁ' => 'ȁ', + 'Ȃ' => 'ȃ', + 'Ȅ' => 'ȅ', + 'Ȇ' => 'ȇ', + 'Ȉ' => 'ȉ', + 'Ȋ' => 'ȋ', + 'Ȍ' => 'ȍ', + 'Ȏ' => 'ȏ', + 'Ȑ' => 'ȑ', + 'Ȓ' => 'ȓ', + 'Ȕ' => 'ȕ', + 'Ȗ' => 'ȗ', + 'Ș' => 'ș', + 'Ț' => 'ț', + 'Ȝ' => 'ȝ', + 'Ȟ' => 'ȟ', + 'Ƞ' => 'ƞ', + 'Ȣ' => 'ȣ', + 'Ȥ' => 'ȥ', + 'Ȧ' => 'ȧ', + 'Ȩ' => 'ȩ', + 'Ȫ' => 'ȫ', + 'Ȭ' => 'ȭ', + 'Ȯ' => 'ȯ', + 'Ȱ' => 'ȱ', + 'Ȳ' => 'ȳ', + 'Ⱥ' => 'ⱥ', + 'Ȼ' => 'ȼ', + 'Ƚ' => 'ƚ', + 'Ⱦ' => 'ⱦ', + 'Ɂ' => 'ɂ', + 'Ƀ' => 'ƀ', + 'Ʉ' => 'ʉ', + 'Ʌ' => 'ʌ', + 'Ɇ' => 'ɇ', + 'Ɉ' => 'ɉ', + 'Ɋ' => 'ɋ', + 'Ɍ' => 'ɍ', + 'Ɏ' => 'ɏ', + 'Ͱ' => 'ͱ', + 'Ͳ' => 'ͳ', + 'Ͷ' => 'ͷ', + 'Ϳ' => 'ϳ', + 'Ά' => 'ά', + 'Έ' => 'έ', + 'Ή' => 'ή', + 'Ί' => 'ί', + 'Ό' => 'ό', + 'Ύ' => 'ύ', + 'Ώ' => 'ώ', + 'Α' => 'α', + 'Β' => 'β', + 'Γ' => 'γ', + 'Δ' => 'δ', + 'Ε' => 'ε', + 'Ζ' => 'ζ', + 'Η' => 'η', + 'Θ' => 'θ', + 'Ι' => 'ι', + 'Κ' => 'κ', + 'Λ' => 'λ', + 'Μ' => 'μ', + 'Ν' => 'ν', + 'Ξ' => 'ξ', + 'Ο' => 'ο', + 'Π' => 'π', + 'Ρ' => 'ρ', + 'Σ' => 'σ', + 'Τ' => 'τ', + 'Υ' => 'υ', + 'Φ' => 'φ', + 'Χ' => 'χ', + 'Ψ' => 'ψ', + 'Ω' => 'ω', + 'Ϊ' => 'ϊ', + 'Ϋ' => 'ϋ', + 'Ϗ' => 'ϗ', + 'Ϙ' => 'ϙ', + 'Ϛ' => 'ϛ', + 'Ϝ' => 'ϝ', + 'Ϟ' => 'ϟ', + 'Ϡ' => 'ϡ', + 'Ϣ' => 'ϣ', + 'Ϥ' => 'ϥ', + 'Ϧ' => 'ϧ', + 'Ϩ' => 'ϩ', + 'Ϫ' => 'ϫ', + 'Ϭ' => 'ϭ', + 'Ϯ' => 'ϯ', + 'ϴ' => 'θ', + 'Ϸ' => 'ϸ', + 'Ϲ' => 'ϲ', + 'Ϻ' => 'ϻ', + 'Ͻ' => 'ͻ', + 'Ͼ' => 'ͼ', + 'Ͽ' => 'ͽ', + 'Ѐ' => 'ѐ', + 'Ё' => 'ё', + 'Ђ' => 'ђ', + 'Ѓ' => 'ѓ', + 'Є' => 'є', + 'Ѕ' => 'ѕ', + 'І' => 'і', + 'Ї' => 'ї', + 'Ј' => 'ј', + 'Љ' => 'љ', + 'Њ' => 'њ', + 'Ћ' => 'ћ', + 'Ќ' => 'ќ', + 'Ѝ' => 'ѝ', + 'Ў' => 'ў', + 'Џ' => 'џ', + 'А' => 'а', + 'Б' => 'б', + 'В' => 'в', + 'Г' => 'г', + 'Д' => 'д', + 'Е' => 'е', + 'Ж' => 'ж', + 'З' => 'з', + 'И' => 'и', + 'Й' => 'й', + 'К' => 'к', + 'Л' => 'л', + 'М' => 'м', + 'Н' => 'н', + 'О' => 'о', + 'П' => 'п', + 'Р' => 'р', + 'С' => 'с', + 'Т' => 'т', + 'У' => 'у', + 'Ф' => 'ф', + 'Х' => 'х', + 'Ц' => 'ц', + 'Ч' => 'ч', + 'Ш' => 'ш', + 'Щ' => 'щ', + 'Ъ' => 'ъ', + 'Ы' => 'ы', + 'Ь' => 'ь', + 'Э' => 'э', + 'Ю' => 'ю', + 'Я' => 'я', + 'Ѡ' => 'ѡ', + 'Ѣ' => 'ѣ', + 'Ѥ' => 'ѥ', + 'Ѧ' => 'ѧ', + 'Ѩ' => 'ѩ', + 'Ѫ' => 'ѫ', + 'Ѭ' => 'ѭ', + 'Ѯ' => 'ѯ', + 'Ѱ' => 'ѱ', + 'Ѳ' => 'ѳ', + 'Ѵ' => 'ѵ', + 'Ѷ' => 'ѷ', + 'Ѹ' => 'ѹ', + 'Ѻ' => 'ѻ', + 'Ѽ' => 'ѽ', + 'Ѿ' => 'ѿ', + 'Ҁ' => 'ҁ', + 'Ҋ' => 'ҋ', + 'Ҍ' => 'ҍ', + 'Ҏ' => 'ҏ', + 'Ґ' => 'ґ', + 'Ғ' => 'ғ', + 'Ҕ' => 'ҕ', + 'Җ' => 'җ', + 'Ҙ' => 'ҙ', + 'Қ' => 'қ', + 'Ҝ' => 'ҝ', + 'Ҟ' => 'ҟ', + 'Ҡ' => 'ҡ', + 'Ң' => 'ң', + 'Ҥ' => 'ҥ', + 'Ҧ' => 'ҧ', + 'Ҩ' => 'ҩ', + 'Ҫ' => 'ҫ', + 'Ҭ' => 'ҭ', + 'Ү' => 'ү', + 'Ұ' => 'ұ', + 'Ҳ' => 'ҳ', + 'Ҵ' => 'ҵ', + 'Ҷ' => 'ҷ', + 'Ҹ' => 'ҹ', + 'Һ' => 'һ', + 'Ҽ' => 'ҽ', + 'Ҿ' => 'ҿ', + 'Ӏ' => 'ӏ', + 'Ӂ' => 'ӂ', + 'Ӄ' => 'ӄ', + 'Ӆ' => 'ӆ', + 'Ӈ' => 'ӈ', + 'Ӊ' => 'ӊ', + 'Ӌ' => 'ӌ', + 'Ӎ' => 'ӎ', + 'Ӑ' => 'ӑ', + 'Ӓ' => 'ӓ', + 'Ӕ' => 'ӕ', + 'Ӗ' => 'ӗ', + 'Ә' => 'ә', + 'Ӛ' => 'ӛ', + 'Ӝ' => 'ӝ', + 'Ӟ' => 'ӟ', + 'Ӡ' => 'ӡ', + 'Ӣ' => 'ӣ', + 'Ӥ' => 'ӥ', + 'Ӧ' => 'ӧ', + 'Ө' => 'ө', + 'Ӫ' => 'ӫ', + 'Ӭ' => 'ӭ', + 'Ӯ' => 'ӯ', + 'Ӱ' => 'ӱ', + 'Ӳ' => 'ӳ', + 'Ӵ' => 'ӵ', + 'Ӷ' => 'ӷ', + 'Ӹ' => 'ӹ', + 'Ӻ' => 'ӻ', + 'Ӽ' => 'ӽ', + 'Ӿ' => 'ӿ', + 'Ԁ' => 'ԁ', + 'Ԃ' => 'ԃ', + 'Ԅ' => 'ԅ', + 'Ԇ' => 'ԇ', + 'Ԉ' => 'ԉ', + 'Ԋ' => 'ԋ', + 'Ԍ' => 'ԍ', + 'Ԏ' => 'ԏ', + 'Ԑ' => 'ԑ', + 'Ԓ' => 'ԓ', + 'Ԕ' => 'ԕ', + 'Ԗ' => 'ԗ', + 'Ԙ' => 'ԙ', + 'Ԛ' => 'ԛ', + 'Ԝ' => 'ԝ', + 'Ԟ' => 'ԟ', + 'Ԡ' => 'ԡ', + 'Ԣ' => 'ԣ', + 'Ԥ' => 'ԥ', + 'Ԧ' => 'ԧ', + 'Ԩ' => 'ԩ', + 'Ԫ' => 'ԫ', + 'Ԭ' => 'ԭ', + 'Ԯ' => 'ԯ', + 'Ա' => 'ա', + 'Բ' => 'բ', + 'Գ' => 'գ', + 'Դ' => 'դ', + 'Ե' => 'ե', + 'Զ' => 'զ', + 'Է' => 'է', + 'Ը' => 'ը', + 'Թ' => 'թ', + 'Ժ' => 'ժ', + 'Ի' => 'ի', + 'Լ' => 'լ', + 'Խ' => 'խ', + 'Ծ' => 'ծ', + 'Կ' => 'կ', + 'Հ' => 'հ', + 'Ձ' => 'ձ', + 'Ղ' => 'ղ', + 'Ճ' => 'ճ', + 'Մ' => 'մ', + 'Յ' => 'յ', + 'Ն' => 'ն', + 'Շ' => 'շ', + 'Ո' => 'ո', + 'Չ' => 'չ', + 'Պ' => 'պ', + 'Ջ' => 'ջ', + 'Ռ' => 'ռ', + 'Ս' => 'ս', + 'Վ' => 'վ', + 'Տ' => 'տ', + 'Ր' => 'ր', + 'Ց' => 'ց', + 'Ւ' => 'ւ', + 'Փ' => 'փ', + 'Ք' => 'ք', + 'Օ' => 'օ', + 'Ֆ' => 'ֆ', + 'Ⴀ' => 'ⴀ', + 'Ⴁ' => 'ⴁ', + 'Ⴂ' => 'ⴂ', + 'Ⴃ' => 'ⴃ', + 'Ⴄ' => 'ⴄ', + 'Ⴅ' => 'ⴅ', + 'Ⴆ' => 'ⴆ', + 'Ⴇ' => 'ⴇ', + 'Ⴈ' => 'ⴈ', + 'Ⴉ' => 'ⴉ', + 'Ⴊ' => 'ⴊ', + 'Ⴋ' => 'ⴋ', + 'Ⴌ' => 'ⴌ', + 'Ⴍ' => 'ⴍ', + 'Ⴎ' => 'ⴎ', + 'Ⴏ' => 'ⴏ', + 'Ⴐ' => 'ⴐ', + 'Ⴑ' => 'ⴑ', + 'Ⴒ' => 'ⴒ', + 'Ⴓ' => 'ⴓ', + 'Ⴔ' => 'ⴔ', + 'Ⴕ' => 'ⴕ', + 'Ⴖ' => 'ⴖ', + 'Ⴗ' => 'ⴗ', + 'Ⴘ' => 'ⴘ', + 'Ⴙ' => 'ⴙ', + 'Ⴚ' => 'ⴚ', + 'Ⴛ' => 'ⴛ', + 'Ⴜ' => 'ⴜ', + 'Ⴝ' => 'ⴝ', + 'Ⴞ' => 'ⴞ', + 'Ⴟ' => 'ⴟ', + 'Ⴠ' => 'ⴠ', + 'Ⴡ' => 'ⴡ', + 'Ⴢ' => 'ⴢ', + 'Ⴣ' => 'ⴣ', + 'Ⴤ' => 'ⴤ', + 'Ⴥ' => 'ⴥ', + 'Ⴧ' => 'ⴧ', + 'Ⴭ' => 'ⴭ', + 'Ḁ' => 'ḁ', + 'Ḃ' => 'ḃ', + 'Ḅ' => 'ḅ', + 'Ḇ' => 'ḇ', + 'Ḉ' => 'ḉ', + 'Ḋ' => 'ḋ', + 'Ḍ' => 'ḍ', + 'Ḏ' => 'ḏ', + 'Ḑ' => 'ḑ', + 'Ḓ' => 'ḓ', + 'Ḕ' => 'ḕ', + 'Ḗ' => 'ḗ', + 'Ḙ' => 'ḙ', + 'Ḛ' => 'ḛ', + 'Ḝ' => 'ḝ', + 'Ḟ' => 'ḟ', + 'Ḡ' => 'ḡ', + 'Ḣ' => 'ḣ', + 'Ḥ' => 'ḥ', + 'Ḧ' => 'ḧ', + 'Ḩ' => 'ḩ', + 'Ḫ' => 'ḫ', + 'Ḭ' => 'ḭ', + 'Ḯ' => 'ḯ', + 'Ḱ' => 'ḱ', + 'Ḳ' => 'ḳ', + 'Ḵ' => 'ḵ', + 'Ḷ' => 'ḷ', + 'Ḹ' => 'ḹ', + 'Ḻ' => 'ḻ', + 'Ḽ' => 'ḽ', + 'Ḿ' => 'ḿ', + 'Ṁ' => 'ṁ', + 'Ṃ' => 'ṃ', + 'Ṅ' => 'ṅ', + 'Ṇ' => 'ṇ', + 'Ṉ' => 'ṉ', + 'Ṋ' => 'ṋ', + 'Ṍ' => 'ṍ', + 'Ṏ' => 'ṏ', + 'Ṑ' => 'ṑ', + 'Ṓ' => 'ṓ', + 'Ṕ' => 'ṕ', + 'Ṗ' => 'ṗ', + 'Ṙ' => 'ṙ', + 'Ṛ' => 'ṛ', + 'Ṝ' => 'ṝ', + 'Ṟ' => 'ṟ', + 'Ṡ' => 'ṡ', + 'Ṣ' => 'ṣ', + 'Ṥ' => 'ṥ', + 'Ṧ' => 'ṧ', + 'Ṩ' => 'ṩ', + 'Ṫ' => 'ṫ', + 'Ṭ' => 'ṭ', + 'Ṯ' => 'ṯ', + 'Ṱ' => 'ṱ', + 'Ṳ' => 'ṳ', + 'Ṵ' => 'ṵ', + 'Ṷ' => 'ṷ', + 'Ṹ' => 'ṹ', + 'Ṻ' => 'ṻ', + 'Ṽ' => 'ṽ', + 'Ṿ' => 'ṿ', + 'Ẁ' => 'ẁ', + 'Ẃ' => 'ẃ', + 'Ẅ' => 'ẅ', + 'Ẇ' => 'ẇ', + 'Ẉ' => 'ẉ', + 'Ẋ' => 'ẋ', + 'Ẍ' => 'ẍ', + 'Ẏ' => 'ẏ', + 'Ẑ' => 'ẑ', + 'Ẓ' => 'ẓ', + 'Ẕ' => 'ẕ', + 'ẞ' => 'ß', + 'Ạ' => 'ạ', + 'Ả' => 'ả', + 'Ấ' => 'ấ', + 'Ầ' => 'ầ', + 'Ẩ' => 'ẩ', + 'Ẫ' => 'ẫ', + 'Ậ' => 'ậ', + 'Ắ' => 'ắ', + 'Ằ' => 'ằ', + 'Ẳ' => 'ẳ', + 'Ẵ' => 'ẵ', + 'Ặ' => 'ặ', + 'Ẹ' => 'ẹ', + 'Ẻ' => 'ẻ', + 'Ẽ' => 'ẽ', + 'Ế' => 'ế', + 'Ề' => 'ề', + 'Ể' => 'ể', + 'Ễ' => 'ễ', + 'Ệ' => 'ệ', + 'Ỉ' => 'ỉ', + 'Ị' => 'ị', + 'Ọ' => 'ọ', + 'Ỏ' => 'ỏ', + 'Ố' => 'ố', + 'Ồ' => 'ồ', + 'Ổ' => 'ổ', + 'Ỗ' => 'ỗ', + 'Ộ' => 'ộ', + 'Ớ' => 'ớ', + 'Ờ' => 'ờ', + 'Ở' => 'ở', + 'Ỡ' => 'ỡ', + 'Ợ' => 'ợ', + 'Ụ' => 'ụ', + 'Ủ' => 'ủ', + 'Ứ' => 'ứ', + 'Ừ' => 'ừ', + 'Ử' => 'ử', + 'Ữ' => 'ữ', + 'Ự' => 'ự', + 'Ỳ' => 'ỳ', + 'Ỵ' => 'ỵ', + 'Ỷ' => 'ỷ', + 'Ỹ' => 'ỹ', + 'Ỻ' => 'ỻ', + 'Ỽ' => 'ỽ', + 'Ỿ' => 'ỿ', + 'Ἀ' => 'ἀ', + 'Ἁ' => 'ἁ', + 'Ἂ' => 'ἂ', + 'Ἃ' => 'ἃ', + 'Ἄ' => 'ἄ', + 'Ἅ' => 'ἅ', + 'Ἆ' => 'ἆ', + 'Ἇ' => 'ἇ', + 'Ἐ' => 'ἐ', + 'Ἑ' => 'ἑ', + 'Ἒ' => 'ἒ', + 'Ἓ' => 'ἓ', + 'Ἔ' => 'ἔ', + 'Ἕ' => 'ἕ', + 'Ἠ' => 'ἠ', + 'Ἡ' => 'ἡ', + 'Ἢ' => 'ἢ', + 'Ἣ' => 'ἣ', + 'Ἤ' => 'ἤ', + 'Ἥ' => 'ἥ', + 'Ἦ' => 'ἦ', + 'Ἧ' => 'ἧ', + 'Ἰ' => 'ἰ', + 'Ἱ' => 'ἱ', + 'Ἲ' => 'ἲ', + 'Ἳ' => 'ἳ', + 'Ἴ' => 'ἴ', + 'Ἵ' => 'ἵ', + 'Ἶ' => 'ἶ', + 'Ἷ' => 'ἷ', + 'Ὀ' => 'ὀ', + 'Ὁ' => 'ὁ', + 'Ὂ' => 'ὂ', + 'Ὃ' => 'ὃ', + 'Ὄ' => 'ὄ', + 'Ὅ' => 'ὅ', + 'Ὑ' => 'ὑ', + 'Ὓ' => 'ὓ', + 'Ὕ' => 'ὕ', + 'Ὗ' => 'ὗ', + 'Ὠ' => 'ὠ', + 'Ὡ' => 'ὡ', + 'Ὢ' => 'ὢ', + 'Ὣ' => 'ὣ', + 'Ὤ' => 'ὤ', + 'Ὥ' => 'ὥ', + 'Ὦ' => 'ὦ', + 'Ὧ' => 'ὧ', + 'ᾈ' => 'ᾀ', + 'ᾉ' => 'ᾁ', + 'ᾊ' => 'ᾂ', + 'ᾋ' => 'ᾃ', + 'ᾌ' => 'ᾄ', + 'ᾍ' => 'ᾅ', + 'ᾎ' => 'ᾆ', + 'ᾏ' => 'ᾇ', + 'ᾘ' => 'ᾐ', + 'ᾙ' => 'ᾑ', + 'ᾚ' => 'ᾒ', + 'ᾛ' => 'ᾓ', + 'ᾜ' => 'ᾔ', + 'ᾝ' => 'ᾕ', + 'ᾞ' => 'ᾖ', + 'ᾟ' => 'ᾗ', + 'ᾨ' => 'ᾠ', + 'ᾩ' => 'ᾡ', + 'ᾪ' => 'ᾢ', + 'ᾫ' => 'ᾣ', + 'ᾬ' => 'ᾤ', + 'ᾭ' => 'ᾥ', + 'ᾮ' => 'ᾦ', + 'ᾯ' => 'ᾧ', + 'Ᾰ' => 'ᾰ', + 'Ᾱ' => 'ᾱ', + 'Ὰ' => 'ὰ', + 'Ά' => 'ά', + 'ᾼ' => 'ᾳ', + 'Ὲ' => 'ὲ', + 'Έ' => 'έ', + 'Ὴ' => 'ὴ', + 'Ή' => 'ή', + 'ῌ' => 'ῃ', + 'Ῐ' => 'ῐ', + 'Ῑ' => 'ῑ', + 'Ὶ' => 'ὶ', + 'Ί' => 'ί', + 'Ῠ' => 'ῠ', + 'Ῡ' => 'ῡ', + 'Ὺ' => 'ὺ', + 'Ύ' => 'ύ', + 'Ῥ' => 'ῥ', + 'Ὸ' => 'ὸ', + 'Ό' => 'ό', + 'Ὼ' => 'ὼ', + 'Ώ' => 'ώ', + 'ῼ' => 'ῳ', + 'Ω' => 'ω', + 'K' => 'k', + 'Å' => 'å', + 'Ⅎ' => 'ⅎ', + 'Ⅰ' => 'ⅰ', + 'Ⅱ' => 'ⅱ', + 'Ⅲ' => 'ⅲ', + 'Ⅳ' => 'ⅳ', + 'Ⅴ' => 'ⅴ', + 'Ⅵ' => 'ⅵ', + 'Ⅶ' => 'ⅶ', + 'Ⅷ' => 'ⅷ', + 'Ⅸ' => 'ⅸ', + 'Ⅹ' => 'ⅹ', + 'Ⅺ' => 'ⅺ', + 'Ⅻ' => 'ⅻ', + 'Ⅼ' => 'ⅼ', + 'Ⅽ' => 'ⅽ', + 'Ⅾ' => 'ⅾ', + 'Ⅿ' => 'ⅿ', + 'Ↄ' => 'ↄ', + 'Ⓐ' => 'ⓐ', + 'Ⓑ' => 'ⓑ', + 'Ⓒ' => 'ⓒ', + 'Ⓓ' => 'ⓓ', + 'Ⓔ' => 'ⓔ', + 'Ⓕ' => 'ⓕ', + 'Ⓖ' => 'ⓖ', + 'Ⓗ' => 'ⓗ', + 'Ⓘ' => 'ⓘ', + 'Ⓙ' => 'ⓙ', + 'Ⓚ' => 'ⓚ', + 'Ⓛ' => 'ⓛ', + 'Ⓜ' => 'ⓜ', + 'Ⓝ' => 'ⓝ', + 'Ⓞ' => 'ⓞ', + 'Ⓟ' => 'ⓟ', + 'Ⓠ' => 'ⓠ', + 'Ⓡ' => 'ⓡ', + 'Ⓢ' => 'ⓢ', + 'Ⓣ' => 'ⓣ', + 'Ⓤ' => 'ⓤ', + 'Ⓥ' => 'ⓥ', + 'Ⓦ' => 'ⓦ', + 'Ⓧ' => 'ⓧ', + 'Ⓨ' => 'ⓨ', + 'Ⓩ' => 'ⓩ', + 'Ⰰ' => 'ⰰ', + 'Ⰱ' => 'ⰱ', + 'Ⰲ' => 'ⰲ', + 'Ⰳ' => 'ⰳ', + 'Ⰴ' => 'ⰴ', + 'Ⰵ' => 'ⰵ', + 'Ⰶ' => 'ⰶ', + 'Ⰷ' => 'ⰷ', + 'Ⰸ' => 'ⰸ', + 'Ⰹ' => 'ⰹ', + 'Ⰺ' => 'ⰺ', + 'Ⰻ' => 'ⰻ', + 'Ⰼ' => 'ⰼ', + 'Ⰽ' => 'ⰽ', + 'Ⰾ' => 'ⰾ', + 'Ⰿ' => 'ⰿ', + 'Ⱀ' => 'ⱀ', + 'Ⱁ' => 'ⱁ', + 'Ⱂ' => 'ⱂ', + 'Ⱃ' => 'ⱃ', + 'Ⱄ' => 'ⱄ', + 'Ⱅ' => 'ⱅ', + 'Ⱆ' => 'ⱆ', + 'Ⱇ' => 'ⱇ', + 'Ⱈ' => 'ⱈ', + 'Ⱉ' => 'ⱉ', + 'Ⱊ' => 'ⱊ', + 'Ⱋ' => 'ⱋ', + 'Ⱌ' => 'ⱌ', + 'Ⱍ' => 'ⱍ', + 'Ⱎ' => 'ⱎ', + 'Ⱏ' => 'ⱏ', + 'Ⱐ' => 'ⱐ', + 'Ⱑ' => 'ⱑ', + 'Ⱒ' => 'ⱒ', + 'Ⱓ' => 'ⱓ', + 'Ⱔ' => 'ⱔ', + 'Ⱕ' => 'ⱕ', + 'Ⱖ' => 'ⱖ', + 'Ⱗ' => 'ⱗ', + 'Ⱘ' => 'ⱘ', + 'Ⱙ' => 'ⱙ', + 'Ⱚ' => 'ⱚ', + 'Ⱛ' => 'ⱛ', + 'Ⱜ' => 'ⱜ', + 'Ⱝ' => 'ⱝ', + 'Ⱞ' => 'ⱞ', + 'Ⱡ' => 'ⱡ', + 'Ɫ' => 'ɫ', + 'Ᵽ' => 'ᵽ', + 'Ɽ' => 'ɽ', + 'Ⱨ' => 'ⱨ', + 'Ⱪ' => 'ⱪ', + 'Ⱬ' => 'ⱬ', + 'Ɑ' => 'ɑ', + 'Ɱ' => 'ɱ', + 'Ɐ' => 'ɐ', + 'Ɒ' => 'ɒ', + 'Ⱳ' => 'ⱳ', + 'Ⱶ' => 'ⱶ', + 'Ȿ' => 'ȿ', + 'Ɀ' => 'ɀ', + 'Ⲁ' => 'ⲁ', + 'Ⲃ' => 'ⲃ', + 'Ⲅ' => 'ⲅ', + 'Ⲇ' => 'ⲇ', + 'Ⲉ' => 'ⲉ', + 'Ⲋ' => 'ⲋ', + 'Ⲍ' => 'ⲍ', + 'Ⲏ' => 'ⲏ', + 'Ⲑ' => 'ⲑ', + 'Ⲓ' => 'ⲓ', + 'Ⲕ' => 'ⲕ', + 'Ⲗ' => 'ⲗ', + 'Ⲙ' => 'ⲙ', + 'Ⲛ' => 'ⲛ', + 'Ⲝ' => 'ⲝ', + 'Ⲟ' => 'ⲟ', + 'Ⲡ' => 'ⲡ', + 'Ⲣ' => 'ⲣ', + 'Ⲥ' => 'ⲥ', + 'Ⲧ' => 'ⲧ', + 'Ⲩ' => 'ⲩ', + 'Ⲫ' => 'ⲫ', + 'Ⲭ' => 'ⲭ', + 'Ⲯ' => 'ⲯ', + 'Ⲱ' => 'ⲱ', + 'Ⲳ' => 'ⲳ', + 'Ⲵ' => 'ⲵ', + 'Ⲷ' => 'ⲷ', + 'Ⲹ' => 'ⲹ', + 'Ⲻ' => 'ⲻ', + 'Ⲽ' => 'ⲽ', + 'Ⲿ' => 'ⲿ', + 'Ⳁ' => 'ⳁ', + 'Ⳃ' => 'ⳃ', + 'Ⳅ' => 'ⳅ', + 'Ⳇ' => 'ⳇ', + 'Ⳉ' => 'ⳉ', + 'Ⳋ' => 'ⳋ', + 'Ⳍ' => 'ⳍ', + 'Ⳏ' => 'ⳏ', + 'Ⳑ' => 'ⳑ', + 'Ⳓ' => 'ⳓ', + 'Ⳕ' => 'ⳕ', + 'Ⳗ' => 'ⳗ', + 'Ⳙ' => 'ⳙ', + 'Ⳛ' => 'ⳛ', + 'Ⳝ' => 'ⳝ', + 'Ⳟ' => 'ⳟ', + 'Ⳡ' => 'ⳡ', + 'Ⳣ' => 'ⳣ', + 'Ⳬ' => 'ⳬ', + 'Ⳮ' => 'ⳮ', + 'Ⳳ' => 'ⳳ', + 'Ꙁ' => 'ꙁ', + 'Ꙃ' => 'ꙃ', + 'Ꙅ' => 'ꙅ', + 'Ꙇ' => 'ꙇ', + 'Ꙉ' => 'ꙉ', + 'Ꙋ' => 'ꙋ', + 'Ꙍ' => 'ꙍ', + 'Ꙏ' => 'ꙏ', + 'Ꙑ' => 'ꙑ', + 'Ꙓ' => 'ꙓ', + 'Ꙕ' => 'ꙕ', + 'Ꙗ' => 'ꙗ', + 'Ꙙ' => 'ꙙ', + 'Ꙛ' => 'ꙛ', + 'Ꙝ' => 'ꙝ', + 'Ꙟ' => 'ꙟ', + 'Ꙡ' => 'ꙡ', + 'Ꙣ' => 'ꙣ', + 'Ꙥ' => 'ꙥ', + 'Ꙧ' => 'ꙧ', + 'Ꙩ' => 'ꙩ', + 'Ꙫ' => 'ꙫ', + 'Ꙭ' => 'ꙭ', + 'Ꚁ' => 'ꚁ', + 'Ꚃ' => 'ꚃ', + 'Ꚅ' => 'ꚅ', + 'Ꚇ' => 'ꚇ', + 'Ꚉ' => 'ꚉ', + 'Ꚋ' => 'ꚋ', + 'Ꚍ' => 'ꚍ', + 'Ꚏ' => 'ꚏ', + 'Ꚑ' => 'ꚑ', + 'Ꚓ' => 'ꚓ', + 'Ꚕ' => 'ꚕ', + 'Ꚗ' => 'ꚗ', + 'Ꚙ' => 'ꚙ', + 'Ꚛ' => 'ꚛ', + 'Ꜣ' => 'ꜣ', + 'Ꜥ' => 'ꜥ', + 'Ꜧ' => 'ꜧ', + 'Ꜩ' => 'ꜩ', + 'Ꜫ' => 'ꜫ', + 'Ꜭ' => 'ꜭ', + 'Ꜯ' => 'ꜯ', + 'Ꜳ' => 'ꜳ', + 'Ꜵ' => 'ꜵ', + 'Ꜷ' => 'ꜷ', + 'Ꜹ' => 'ꜹ', + 'Ꜻ' => 'ꜻ', + 'Ꜽ' => 'ꜽ', + 'Ꜿ' => 'ꜿ', + 'Ꝁ' => 'ꝁ', + 'Ꝃ' => 'ꝃ', + 'Ꝅ' => 'ꝅ', + 'Ꝇ' => 'ꝇ', + 'Ꝉ' => 'ꝉ', + 'Ꝋ' => 'ꝋ', + 'Ꝍ' => 'ꝍ', + 'Ꝏ' => 'ꝏ', + 'Ꝑ' => 'ꝑ', + 'Ꝓ' => 'ꝓ', + 'Ꝕ' => 'ꝕ', + 'Ꝗ' => 'ꝗ', + 'Ꝙ' => 'ꝙ', + 'Ꝛ' => 'ꝛ', + 'Ꝝ' => 'ꝝ', + 'Ꝟ' => 'ꝟ', + 'Ꝡ' => 'ꝡ', + 'Ꝣ' => 'ꝣ', + 'Ꝥ' => 'ꝥ', + 'Ꝧ' => 'ꝧ', + 'Ꝩ' => 'ꝩ', + 'Ꝫ' => 'ꝫ', + 'Ꝭ' => 'ꝭ', + 'Ꝯ' => 'ꝯ', + 'Ꝺ' => 'ꝺ', + 'Ꝼ' => 'ꝼ', + 'Ᵹ' => 'ᵹ', + 'Ꝿ' => 'ꝿ', + 'Ꞁ' => 'ꞁ', + 'Ꞃ' => 'ꞃ', + 'Ꞅ' => 'ꞅ', + 'Ꞇ' => 'ꞇ', + 'Ꞌ' => 'ꞌ', + 'Ɥ' => 'ɥ', + 'Ꞑ' => 'ꞑ', + 'Ꞓ' => 'ꞓ', + 'Ꞗ' => 'ꞗ', + 'Ꞙ' => 'ꞙ', + 'Ꞛ' => 'ꞛ', + 'Ꞝ' => 'ꞝ', + 'Ꞟ' => 'ꞟ', + 'Ꞡ' => 'ꞡ', + 'Ꞣ' => 'ꞣ', + 'Ꞥ' => 'ꞥ', + 'Ꞧ' => 'ꞧ', + 'Ꞩ' => 'ꞩ', + 'Ɦ' => 'ɦ', + 'Ɜ' => 'ɜ', + 'Ɡ' => 'ɡ', + 'Ɬ' => 'ɬ', + 'Ʞ' => 'ʞ', + 'Ʇ' => 'ʇ', + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + '𐐀' => '𐐨', + '𐐁' => '𐐩', + '𐐂' => '𐐪', + '𐐃' => '𐐫', + '𐐄' => '𐐬', + '𐐅' => '𐐭', + '𐐆' => '𐐮', + '𐐇' => '𐐯', + '𐐈' => '𐐰', + '𐐉' => '𐐱', + '𐐊' => '𐐲', + '𐐋' => '𐐳', + '𐐌' => '𐐴', + '𐐍' => '𐐵', + '𐐎' => '𐐶', + '𐐏' => '𐐷', + '𐐐' => '𐐸', + '𐐑' => '𐐹', + '𐐒' => '𐐺', + '𐐓' => '𐐻', + '𐐔' => '𐐼', + '𐐕' => '𐐽', + '𐐖' => '𐐾', + '𐐗' => '𐐿', + '𐐘' => '𐑀', + '𐐙' => '𐑁', + '𐐚' => '𐑂', + '𐐛' => '𐑃', + '𐐜' => '𐑄', + '𐐝' => '𐑅', + '𐐞' => '𐑆', + '𐐟' => '𐑇', + '𐐠' => '𐑈', + '𐐡' => '𐑉', + '𐐢' => '𐑊', + '𐐣' => '𐑋', + '𐐤' => '𐑌', + '𐐥' => '𐑍', + '𐐦' => '𐑎', + '𐐧' => '𐑏', + '𑢠' => '𑣀', + '𑢡' => '𑣁', + '𑢢' => '𑣂', + '𑢣' => '𑣃', + '𑢤' => '𑣄', + '𑢥' => '𑣅', + '𑢦' => '𑣆', + '𑢧' => '𑣇', + '𑢨' => '𑣈', + '𑢩' => '𑣉', + '𑢪' => '𑣊', + '𑢫' => '𑣋', + '𑢬' => '𑣌', + '𑢭' => '𑣍', + '𑢮' => '𑣎', + '𑢯' => '𑣏', + '𑢰' => '𑣐', + '𑢱' => '𑣑', + '𑢲' => '𑣒', + '𑢳' => '𑣓', + '𑢴' => '𑣔', + '𑢵' => '𑣕', + '𑢶' => '𑣖', + '𑢷' => '𑣗', + '𑢸' => '𑣘', + '𑢹' => '𑣙', + '𑢺' => '𑣚', + '𑢻' => '𑣛', + '𑢼' => '𑣜', + '𑢽' => '𑣝', + '𑢾' => '𑣞', + '𑢿' => '𑣟', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php new file mode 100644 index 00000000000..ec9422121ca --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php @@ -0,0 +1,1109 @@ + 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + 'µ' => 'Μ', + 'à' => 'À', + 'á' => 'Á', + 'â' => 'Â', + 'ã' => 'Ã', + 'ä' => 'Ä', + 'å' => 'Å', + 'æ' => 'Æ', + 'ç' => 'Ç', + 'è' => 'È', + 'é' => 'É', + 'ê' => 'Ê', + 'ë' => 'Ë', + 'ì' => 'Ì', + 'í' => 'Í', + 'î' => 'Î', + 'ï' => 'Ï', + 'ð' => 'Ð', + 'ñ' => 'Ñ', + 'ò' => 'Ò', + 'ó' => 'Ó', + 'ô' => 'Ô', + 'õ' => 'Õ', + 'ö' => 'Ö', + 'ø' => 'Ø', + 'ù' => 'Ù', + 'ú' => 'Ú', + 'û' => 'Û', + 'ü' => 'Ü', + 'ý' => 'Ý', + 'þ' => 'Þ', + 'ÿ' => 'Ÿ', + 'ā' => 'Ā', + 'ă' => 'Ă', + 'ą' => 'Ą', + 'ć' => 'Ć', + 'ĉ' => 'Ĉ', + 'ċ' => 'Ċ', + 'č' => 'Č', + 'ď' => 'Ď', + 'đ' => 'Đ', + 'ē' => 'Ē', + 'ĕ' => 'Ĕ', + 'ė' => 'Ė', + 'ę' => 'Ę', + 'ě' => 'Ě', + 'ĝ' => 'Ĝ', + 'ğ' => 'Ğ', + 'ġ' => 'Ġ', + 'ģ' => 'Ģ', + 'ĥ' => 'Ĥ', + 'ħ' => 'Ħ', + 'ĩ' => 'Ĩ', + 'ī' => 'Ī', + 'ĭ' => 'Ĭ', + 'į' => 'Į', + 'ı' => 'I', + 'ij' => 'IJ', + 'ĵ' => 'Ĵ', + 'ķ' => 'Ķ', + 'ĺ' => 'Ĺ', + 'ļ' => 'Ļ', + 'ľ' => 'Ľ', + 'ŀ' => 'Ŀ', + 'ł' => 'Ł', + 'ń' => 'Ń', + 'ņ' => 'Ņ', + 'ň' => 'Ň', + 'ŋ' => 'Ŋ', + 'ō' => 'Ō', + 'ŏ' => 'Ŏ', + 'ő' => 'Ő', + 'œ' => 'Œ', + 'ŕ' => 'Ŕ', + 'ŗ' => 'Ŗ', + 'ř' => 'Ř', + 'ś' => 'Ś', + 'ŝ' => 'Ŝ', + 'ş' => 'Ş', + 'š' => 'Š', + 'ţ' => 'Ţ', + 'ť' => 'Ť', + 'ŧ' => 'Ŧ', + 'ũ' => 'Ũ', + 'ū' => 'Ū', + 'ŭ' => 'Ŭ', + 'ů' => 'Ů', + 'ű' => 'Ű', + 'ų' => 'Ų', + 'ŵ' => 'Ŵ', + 'ŷ' => 'Ŷ', + 'ź' => 'Ź', + 'ż' => 'Ż', + 'ž' => 'Ž', + 'ſ' => 'S', + 'ƀ' => 'Ƀ', + 'ƃ' => 'Ƃ', + 'ƅ' => 'Ƅ', + 'ƈ' => 'Ƈ', + 'ƌ' => 'Ƌ', + 'ƒ' => 'Ƒ', + 'ƕ' => 'Ƕ', + 'ƙ' => 'Ƙ', + 'ƚ' => 'Ƚ', + 'ƞ' => 'Ƞ', + 'ơ' => 'Ơ', + 'ƣ' => 'Ƣ', + 'ƥ' => 'Ƥ', + 'ƨ' => 'Ƨ', + 'ƭ' => 'Ƭ', + 'ư' => 'Ư', + 'ƴ' => 'Ƴ', + 'ƶ' => 'Ƶ', + 'ƹ' => 'Ƹ', + 'ƽ' => 'Ƽ', + 'ƿ' => 'Ƿ', + 'Dž' => 'DŽ', + 'dž' => 'DŽ', + 'Lj' => 'LJ', + 'lj' => 'LJ', + 'Nj' => 'NJ', + 'nj' => 'NJ', + 'ǎ' => 'Ǎ', + 'ǐ' => 'Ǐ', + 'ǒ' => 'Ǒ', + 'ǔ' => 'Ǔ', + 'ǖ' => 'Ǖ', + 'ǘ' => 'Ǘ', + 'ǚ' => 'Ǚ', + 'ǜ' => 'Ǜ', + 'ǝ' => 'Ǝ', + 'ǟ' => 'Ǟ', + 'ǡ' => 'Ǡ', + 'ǣ' => 'Ǣ', + 'ǥ' => 'Ǥ', + 'ǧ' => 'Ǧ', + 'ǩ' => 'Ǩ', + 'ǫ' => 'Ǫ', + 'ǭ' => 'Ǭ', + 'ǯ' => 'Ǯ', + 'Dz' => 'DZ', + 'dz' => 'DZ', + 'ǵ' => 'Ǵ', + 'ǹ' => 'Ǹ', + 'ǻ' => 'Ǻ', + 'ǽ' => 'Ǽ', + 'ǿ' => 'Ǿ', + 'ȁ' => 'Ȁ', + 'ȃ' => 'Ȃ', + 'ȅ' => 'Ȅ', + 'ȇ' => 'Ȇ', + 'ȉ' => 'Ȉ', + 'ȋ' => 'Ȋ', + 'ȍ' => 'Ȍ', + 'ȏ' => 'Ȏ', + 'ȑ' => 'Ȑ', + 'ȓ' => 'Ȓ', + 'ȕ' => 'Ȕ', + 'ȗ' => 'Ȗ', + 'ș' => 'Ș', + 'ț' => 'Ț', + 'ȝ' => 'Ȝ', + 'ȟ' => 'Ȟ', + 'ȣ' => 'Ȣ', + 'ȥ' => 'Ȥ', + 'ȧ' => 'Ȧ', + 'ȩ' => 'Ȩ', + 'ȫ' => 'Ȫ', + 'ȭ' => 'Ȭ', + 'ȯ' => 'Ȯ', + 'ȱ' => 'Ȱ', + 'ȳ' => 'Ȳ', + 'ȼ' => 'Ȼ', + 'ȿ' => 'Ȿ', + 'ɀ' => 'Ɀ', + 'ɂ' => 'Ɂ', + 'ɇ' => 'Ɇ', + 'ɉ' => 'Ɉ', + 'ɋ' => 'Ɋ', + 'ɍ' => 'Ɍ', + 'ɏ' => 'Ɏ', + 'ɐ' => 'Ɐ', + 'ɑ' => 'Ɑ', + 'ɒ' => 'Ɒ', + 'ɓ' => 'Ɓ', + 'ɔ' => 'Ɔ', + 'ɖ' => 'Ɖ', + 'ɗ' => 'Ɗ', + 'ə' => 'Ə', + 'ɛ' => 'Ɛ', + 'ɜ' => 'Ɜ', + 'ɠ' => 'Ɠ', + 'ɡ' => 'Ɡ', + 'ɣ' => 'Ɣ', + 'ɥ' => 'Ɥ', + 'ɦ' => 'Ɦ', + 'ɨ' => 'Ɨ', + 'ɩ' => 'Ɩ', + 'ɫ' => 'Ɫ', + 'ɬ' => 'Ɬ', + 'ɯ' => 'Ɯ', + 'ɱ' => 'Ɱ', + 'ɲ' => 'Ɲ', + 'ɵ' => 'Ɵ', + 'ɽ' => 'Ɽ', + 'ʀ' => 'Ʀ', + 'ʃ' => 'Ʃ', + 'ʇ' => 'Ʇ', + 'ʈ' => 'Ʈ', + 'ʉ' => 'Ʉ', + 'ʊ' => 'Ʊ', + 'ʋ' => 'Ʋ', + 'ʌ' => 'Ʌ', + 'ʒ' => 'Ʒ', + 'ʞ' => 'Ʞ', + 'ͅ' => 'Ι', + 'ͱ' => 'Ͱ', + 'ͳ' => 'Ͳ', + 'ͷ' => 'Ͷ', + 'ͻ' => 'Ͻ', + 'ͼ' => 'Ͼ', + 'ͽ' => 'Ͽ', + 'ά' => 'Ά', + 'έ' => 'Έ', + 'ή' => 'Ή', + 'ί' => 'Ί', + 'α' => 'Α', + 'β' => 'Β', + 'γ' => 'Γ', + 'δ' => 'Δ', + 'ε' => 'Ε', + 'ζ' => 'Ζ', + 'η' => 'Η', + 'θ' => 'Θ', + 'ι' => 'Ι', + 'κ' => 'Κ', + 'λ' => 'Λ', + 'μ' => 'Μ', + 'ν' => 'Ν', + 'ξ' => 'Ξ', + 'ο' => 'Ο', + 'π' => 'Π', + 'ρ' => 'Ρ', + 'ς' => 'Σ', + 'σ' => 'Σ', + 'τ' => 'Τ', + 'υ' => 'Υ', + 'φ' => 'Φ', + 'χ' => 'Χ', + 'ψ' => 'Ψ', + 'ω' => 'Ω', + 'ϊ' => 'Ϊ', + 'ϋ' => 'Ϋ', + 'ό' => 'Ό', + 'ύ' => 'Ύ', + 'ώ' => 'Ώ', + 'ϐ' => 'Β', + 'ϑ' => 'Θ', + 'ϕ' => 'Φ', + 'ϖ' => 'Π', + 'ϗ' => 'Ϗ', + 'ϙ' => 'Ϙ', + 'ϛ' => 'Ϛ', + 'ϝ' => 'Ϝ', + 'ϟ' => 'Ϟ', + 'ϡ' => 'Ϡ', + 'ϣ' => 'Ϣ', + 'ϥ' => 'Ϥ', + 'ϧ' => 'Ϧ', + 'ϩ' => 'Ϩ', + 'ϫ' => 'Ϫ', + 'ϭ' => 'Ϭ', + 'ϯ' => 'Ϯ', + 'ϰ' => 'Κ', + 'ϱ' => 'Ρ', + 'ϲ' => 'Ϲ', + 'ϳ' => 'Ϳ', + 'ϵ' => 'Ε', + 'ϸ' => 'Ϸ', + 'ϻ' => 'Ϻ', + 'а' => 'А', + 'б' => 'Б', + 'в' => 'В', + 'г' => 'Г', + 'д' => 'Д', + 'е' => 'Е', + 'ж' => 'Ж', + 'з' => 'З', + 'и' => 'И', + 'й' => 'Й', + 'к' => 'К', + 'л' => 'Л', + 'м' => 'М', + 'н' => 'Н', + 'о' => 'О', + 'п' => 'П', + 'р' => 'Р', + 'с' => 'С', + 'т' => 'Т', + 'у' => 'У', + 'ф' => 'Ф', + 'х' => 'Х', + 'ц' => 'Ц', + 'ч' => 'Ч', + 'ш' => 'Ш', + 'щ' => 'Щ', + 'ъ' => 'Ъ', + 'ы' => 'Ы', + 'ь' => 'Ь', + 'э' => 'Э', + 'ю' => 'Ю', + 'я' => 'Я', + 'ѐ' => 'Ѐ', + 'ё' => 'Ё', + 'ђ' => 'Ђ', + 'ѓ' => 'Ѓ', + 'є' => 'Є', + 'ѕ' => 'Ѕ', + 'і' => 'І', + 'ї' => 'Ї', + 'ј' => 'Ј', + 'љ' => 'Љ', + 'њ' => 'Њ', + 'ћ' => 'Ћ', + 'ќ' => 'Ќ', + 'ѝ' => 'Ѝ', + 'ў' => 'Ў', + 'џ' => 'Џ', + 'ѡ' => 'Ѡ', + 'ѣ' => 'Ѣ', + 'ѥ' => 'Ѥ', + 'ѧ' => 'Ѧ', + 'ѩ' => 'Ѩ', + 'ѫ' => 'Ѫ', + 'ѭ' => 'Ѭ', + 'ѯ' => 'Ѯ', + 'ѱ' => 'Ѱ', + 'ѳ' => 'Ѳ', + 'ѵ' => 'Ѵ', + 'ѷ' => 'Ѷ', + 'ѹ' => 'Ѹ', + 'ѻ' => 'Ѻ', + 'ѽ' => 'Ѽ', + 'ѿ' => 'Ѿ', + 'ҁ' => 'Ҁ', + 'ҋ' => 'Ҋ', + 'ҍ' => 'Ҍ', + 'ҏ' => 'Ҏ', + 'ґ' => 'Ґ', + 'ғ' => 'Ғ', + 'ҕ' => 'Ҕ', + 'җ' => 'Җ', + 'ҙ' => 'Ҙ', + 'қ' => 'Қ', + 'ҝ' => 'Ҝ', + 'ҟ' => 'Ҟ', + 'ҡ' => 'Ҡ', + 'ң' => 'Ң', + 'ҥ' => 'Ҥ', + 'ҧ' => 'Ҧ', + 'ҩ' => 'Ҩ', + 'ҫ' => 'Ҫ', + 'ҭ' => 'Ҭ', + 'ү' => 'Ү', + 'ұ' => 'Ұ', + 'ҳ' => 'Ҳ', + 'ҵ' => 'Ҵ', + 'ҷ' => 'Ҷ', + 'ҹ' => 'Ҹ', + 'һ' => 'Һ', + 'ҽ' => 'Ҽ', + 'ҿ' => 'Ҿ', + 'ӂ' => 'Ӂ', + 'ӄ' => 'Ӄ', + 'ӆ' => 'Ӆ', + 'ӈ' => 'Ӈ', + 'ӊ' => 'Ӊ', + 'ӌ' => 'Ӌ', + 'ӎ' => 'Ӎ', + 'ӏ' => 'Ӏ', + 'ӑ' => 'Ӑ', + 'ӓ' => 'Ӓ', + 'ӕ' => 'Ӕ', + 'ӗ' => 'Ӗ', + 'ә' => 'Ә', + 'ӛ' => 'Ӛ', + 'ӝ' => 'Ӝ', + 'ӟ' => 'Ӟ', + 'ӡ' => 'Ӡ', + 'ӣ' => 'Ӣ', + 'ӥ' => 'Ӥ', + 'ӧ' => 'Ӧ', + 'ө' => 'Ө', + 'ӫ' => 'Ӫ', + 'ӭ' => 'Ӭ', + 'ӯ' => 'Ӯ', + 'ӱ' => 'Ӱ', + 'ӳ' => 'Ӳ', + 'ӵ' => 'Ӵ', + 'ӷ' => 'Ӷ', + 'ӹ' => 'Ӹ', + 'ӻ' => 'Ӻ', + 'ӽ' => 'Ӽ', + 'ӿ' => 'Ӿ', + 'ԁ' => 'Ԁ', + 'ԃ' => 'Ԃ', + 'ԅ' => 'Ԅ', + 'ԇ' => 'Ԇ', + 'ԉ' => 'Ԉ', + 'ԋ' => 'Ԋ', + 'ԍ' => 'Ԍ', + 'ԏ' => 'Ԏ', + 'ԑ' => 'Ԑ', + 'ԓ' => 'Ԓ', + 'ԕ' => 'Ԕ', + 'ԗ' => 'Ԗ', + 'ԙ' => 'Ԙ', + 'ԛ' => 'Ԛ', + 'ԝ' => 'Ԝ', + 'ԟ' => 'Ԟ', + 'ԡ' => 'Ԡ', + 'ԣ' => 'Ԣ', + 'ԥ' => 'Ԥ', + 'ԧ' => 'Ԧ', + 'ԩ' => 'Ԩ', + 'ԫ' => 'Ԫ', + 'ԭ' => 'Ԭ', + 'ԯ' => 'Ԯ', + 'ա' => 'Ա', + 'բ' => 'Բ', + 'գ' => 'Գ', + 'դ' => 'Դ', + 'ե' => 'Ե', + 'զ' => 'Զ', + 'է' => 'Է', + 'ը' => 'Ը', + 'թ' => 'Թ', + 'ժ' => 'Ժ', + 'ի' => 'Ի', + 'լ' => 'Լ', + 'խ' => 'Խ', + 'ծ' => 'Ծ', + 'կ' => 'Կ', + 'հ' => 'Հ', + 'ձ' => 'Ձ', + 'ղ' => 'Ղ', + 'ճ' => 'Ճ', + 'մ' => 'Մ', + 'յ' => 'Յ', + 'ն' => 'Ն', + 'շ' => 'Շ', + 'ո' => 'Ո', + 'չ' => 'Չ', + 'պ' => 'Պ', + 'ջ' => 'Ջ', + 'ռ' => 'Ռ', + 'ս' => 'Ս', + 'վ' => 'Վ', + 'տ' => 'Տ', + 'ր' => 'Ր', + 'ց' => 'Ց', + 'ւ' => 'Ւ', + 'փ' => 'Փ', + 'ք' => 'Ք', + 'օ' => 'Օ', + 'ֆ' => 'Ֆ', + 'ᵹ' => 'Ᵹ', + 'ᵽ' => 'Ᵽ', + 'ḁ' => 'Ḁ', + 'ḃ' => 'Ḃ', + 'ḅ' => 'Ḅ', + 'ḇ' => 'Ḇ', + 'ḉ' => 'Ḉ', + 'ḋ' => 'Ḋ', + 'ḍ' => 'Ḍ', + 'ḏ' => 'Ḏ', + 'ḑ' => 'Ḑ', + 'ḓ' => 'Ḓ', + 'ḕ' => 'Ḕ', + 'ḗ' => 'Ḗ', + 'ḙ' => 'Ḙ', + 'ḛ' => 'Ḛ', + 'ḝ' => 'Ḝ', + 'ḟ' => 'Ḟ', + 'ḡ' => 'Ḡ', + 'ḣ' => 'Ḣ', + 'ḥ' => 'Ḥ', + 'ḧ' => 'Ḧ', + 'ḩ' => 'Ḩ', + 'ḫ' => 'Ḫ', + 'ḭ' => 'Ḭ', + 'ḯ' => 'Ḯ', + 'ḱ' => 'Ḱ', + 'ḳ' => 'Ḳ', + 'ḵ' => 'Ḵ', + 'ḷ' => 'Ḷ', + 'ḹ' => 'Ḹ', + 'ḻ' => 'Ḻ', + 'ḽ' => 'Ḽ', + 'ḿ' => 'Ḿ', + 'ṁ' => 'Ṁ', + 'ṃ' => 'Ṃ', + 'ṅ' => 'Ṅ', + 'ṇ' => 'Ṇ', + 'ṉ' => 'Ṉ', + 'ṋ' => 'Ṋ', + 'ṍ' => 'Ṍ', + 'ṏ' => 'Ṏ', + 'ṑ' => 'Ṑ', + 'ṓ' => 'Ṓ', + 'ṕ' => 'Ṕ', + 'ṗ' => 'Ṗ', + 'ṙ' => 'Ṙ', + 'ṛ' => 'Ṛ', + 'ṝ' => 'Ṝ', + 'ṟ' => 'Ṟ', + 'ṡ' => 'Ṡ', + 'ṣ' => 'Ṣ', + 'ṥ' => 'Ṥ', + 'ṧ' => 'Ṧ', + 'ṩ' => 'Ṩ', + 'ṫ' => 'Ṫ', + 'ṭ' => 'Ṭ', + 'ṯ' => 'Ṯ', + 'ṱ' => 'Ṱ', + 'ṳ' => 'Ṳ', + 'ṵ' => 'Ṵ', + 'ṷ' => 'Ṷ', + 'ṹ' => 'Ṹ', + 'ṻ' => 'Ṻ', + 'ṽ' => 'Ṽ', + 'ṿ' => 'Ṿ', + 'ẁ' => 'Ẁ', + 'ẃ' => 'Ẃ', + 'ẅ' => 'Ẅ', + 'ẇ' => 'Ẇ', + 'ẉ' => 'Ẉ', + 'ẋ' => 'Ẋ', + 'ẍ' => 'Ẍ', + 'ẏ' => 'Ẏ', + 'ẑ' => 'Ẑ', + 'ẓ' => 'Ẓ', + 'ẕ' => 'Ẕ', + 'ẛ' => 'Ṡ', + 'ạ' => 'Ạ', + 'ả' => 'Ả', + 'ấ' => 'Ấ', + 'ầ' => 'Ầ', + 'ẩ' => 'Ẩ', + 'ẫ' => 'Ẫ', + 'ậ' => 'Ậ', + 'ắ' => 'Ắ', + 'ằ' => 'Ằ', + 'ẳ' => 'Ẳ', + 'ẵ' => 'Ẵ', + 'ặ' => 'Ặ', + 'ẹ' => 'Ẹ', + 'ẻ' => 'Ẻ', + 'ẽ' => 'Ẽ', + 'ế' => 'Ế', + 'ề' => 'Ề', + 'ể' => 'Ể', + 'ễ' => 'Ễ', + 'ệ' => 'Ệ', + 'ỉ' => 'Ỉ', + 'ị' => 'Ị', + 'ọ' => 'Ọ', + 'ỏ' => 'Ỏ', + 'ố' => 'Ố', + 'ồ' => 'Ồ', + 'ổ' => 'Ổ', + 'ỗ' => 'Ỗ', + 'ộ' => 'Ộ', + 'ớ' => 'Ớ', + 'ờ' => 'Ờ', + 'ở' => 'Ở', + 'ỡ' => 'Ỡ', + 'ợ' => 'Ợ', + 'ụ' => 'Ụ', + 'ủ' => 'Ủ', + 'ứ' => 'Ứ', + 'ừ' => 'Ừ', + 'ử' => 'Ử', + 'ữ' => 'Ữ', + 'ự' => 'Ự', + 'ỳ' => 'Ỳ', + 'ỵ' => 'Ỵ', + 'ỷ' => 'Ỷ', + 'ỹ' => 'Ỹ', + 'ỻ' => 'Ỻ', + 'ỽ' => 'Ỽ', + 'ỿ' => 'Ỿ', + 'ἀ' => 'Ἀ', + 'ἁ' => 'Ἁ', + 'ἂ' => 'Ἂ', + 'ἃ' => 'Ἃ', + 'ἄ' => 'Ἄ', + 'ἅ' => 'Ἅ', + 'ἆ' => 'Ἆ', + 'ἇ' => 'Ἇ', + 'ἐ' => 'Ἐ', + 'ἑ' => 'Ἑ', + 'ἒ' => 'Ἒ', + 'ἓ' => 'Ἓ', + 'ἔ' => 'Ἔ', + 'ἕ' => 'Ἕ', + 'ἠ' => 'Ἠ', + 'ἡ' => 'Ἡ', + 'ἢ' => 'Ἢ', + 'ἣ' => 'Ἣ', + 'ἤ' => 'Ἤ', + 'ἥ' => 'Ἥ', + 'ἦ' => 'Ἦ', + 'ἧ' => 'Ἧ', + 'ἰ' => 'Ἰ', + 'ἱ' => 'Ἱ', + 'ἲ' => 'Ἲ', + 'ἳ' => 'Ἳ', + 'ἴ' => 'Ἴ', + 'ἵ' => 'Ἵ', + 'ἶ' => 'Ἶ', + 'ἷ' => 'Ἷ', + 'ὀ' => 'Ὀ', + 'ὁ' => 'Ὁ', + 'ὂ' => 'Ὂ', + 'ὃ' => 'Ὃ', + 'ὄ' => 'Ὄ', + 'ὅ' => 'Ὅ', + 'ὑ' => 'Ὑ', + 'ὓ' => 'Ὓ', + 'ὕ' => 'Ὕ', + 'ὗ' => 'Ὗ', + 'ὠ' => 'Ὠ', + 'ὡ' => 'Ὡ', + 'ὢ' => 'Ὢ', + 'ὣ' => 'Ὣ', + 'ὤ' => 'Ὤ', + 'ὥ' => 'Ὥ', + 'ὦ' => 'Ὦ', + 'ὧ' => 'Ὧ', + 'ὰ' => 'Ὰ', + 'ά' => 'Ά', + 'ὲ' => 'Ὲ', + 'έ' => 'Έ', + 'ὴ' => 'Ὴ', + 'ή' => 'Ή', + 'ὶ' => 'Ὶ', + 'ί' => 'Ί', + 'ὸ' => 'Ὸ', + 'ό' => 'Ό', + 'ὺ' => 'Ὺ', + 'ύ' => 'Ύ', + 'ὼ' => 'Ὼ', + 'ώ' => 'Ώ', + 'ᾀ' => 'ᾈ', + 'ᾁ' => 'ᾉ', + 'ᾂ' => 'ᾊ', + 'ᾃ' => 'ᾋ', + 'ᾄ' => 'ᾌ', + 'ᾅ' => 'ᾍ', + 'ᾆ' => 'ᾎ', + 'ᾇ' => 'ᾏ', + 'ᾐ' => 'ᾘ', + 'ᾑ' => 'ᾙ', + 'ᾒ' => 'ᾚ', + 'ᾓ' => 'ᾛ', + 'ᾔ' => 'ᾜ', + 'ᾕ' => 'ᾝ', + 'ᾖ' => 'ᾞ', + 'ᾗ' => 'ᾟ', + 'ᾠ' => 'ᾨ', + 'ᾡ' => 'ᾩ', + 'ᾢ' => 'ᾪ', + 'ᾣ' => 'ᾫ', + 'ᾤ' => 'ᾬ', + 'ᾥ' => 'ᾭ', + 'ᾦ' => 'ᾮ', + 'ᾧ' => 'ᾯ', + 'ᾰ' => 'Ᾰ', + 'ᾱ' => 'Ᾱ', + 'ᾳ' => 'ᾼ', + 'ι' => 'Ι', + 'ῃ' => 'ῌ', + 'ῐ' => 'Ῐ', + 'ῑ' => 'Ῑ', + 'ῠ' => 'Ῠ', + 'ῡ' => 'Ῡ', + 'ῥ' => 'Ῥ', + 'ῳ' => 'ῼ', + 'ⅎ' => 'Ⅎ', + 'ⅰ' => 'Ⅰ', + 'ⅱ' => 'Ⅱ', + 'ⅲ' => 'Ⅲ', + 'ⅳ' => 'Ⅳ', + 'ⅴ' => 'Ⅴ', + 'ⅵ' => 'Ⅵ', + 'ⅶ' => 'Ⅶ', + 'ⅷ' => 'Ⅷ', + 'ⅸ' => 'Ⅸ', + 'ⅹ' => 'Ⅹ', + 'ⅺ' => 'Ⅺ', + 'ⅻ' => 'Ⅻ', + 'ⅼ' => 'Ⅼ', + 'ⅽ' => 'Ⅽ', + 'ⅾ' => 'Ⅾ', + 'ⅿ' => 'Ⅿ', + 'ↄ' => 'Ↄ', + 'ⓐ' => 'Ⓐ', + 'ⓑ' => 'Ⓑ', + 'ⓒ' => 'Ⓒ', + 'ⓓ' => 'Ⓓ', + 'ⓔ' => 'Ⓔ', + 'ⓕ' => 'Ⓕ', + 'ⓖ' => 'Ⓖ', + 'ⓗ' => 'Ⓗ', + 'ⓘ' => 'Ⓘ', + 'ⓙ' => 'Ⓙ', + 'ⓚ' => 'Ⓚ', + 'ⓛ' => 'Ⓛ', + 'ⓜ' => 'Ⓜ', + 'ⓝ' => 'Ⓝ', + 'ⓞ' => 'Ⓞ', + 'ⓟ' => 'Ⓟ', + 'ⓠ' => 'Ⓠ', + 'ⓡ' => 'Ⓡ', + 'ⓢ' => 'Ⓢ', + 'ⓣ' => 'Ⓣ', + 'ⓤ' => 'Ⓤ', + 'ⓥ' => 'Ⓥ', + 'ⓦ' => 'Ⓦ', + 'ⓧ' => 'Ⓧ', + 'ⓨ' => 'Ⓨ', + 'ⓩ' => 'Ⓩ', + 'ⰰ' => 'Ⰰ', + 'ⰱ' => 'Ⰱ', + 'ⰲ' => 'Ⰲ', + 'ⰳ' => 'Ⰳ', + 'ⰴ' => 'Ⰴ', + 'ⰵ' => 'Ⰵ', + 'ⰶ' => 'Ⰶ', + 'ⰷ' => 'Ⰷ', + 'ⰸ' => 'Ⰸ', + 'ⰹ' => 'Ⰹ', + 'ⰺ' => 'Ⰺ', + 'ⰻ' => 'Ⰻ', + 'ⰼ' => 'Ⰼ', + 'ⰽ' => 'Ⰽ', + 'ⰾ' => 'Ⰾ', + 'ⰿ' => 'Ⰿ', + 'ⱀ' => 'Ⱀ', + 'ⱁ' => 'Ⱁ', + 'ⱂ' => 'Ⱂ', + 'ⱃ' => 'Ⱃ', + 'ⱄ' => 'Ⱄ', + 'ⱅ' => 'Ⱅ', + 'ⱆ' => 'Ⱆ', + 'ⱇ' => 'Ⱇ', + 'ⱈ' => 'Ⱈ', + 'ⱉ' => 'Ⱉ', + 'ⱊ' => 'Ⱊ', + 'ⱋ' => 'Ⱋ', + 'ⱌ' => 'Ⱌ', + 'ⱍ' => 'Ⱍ', + 'ⱎ' => 'Ⱎ', + 'ⱏ' => 'Ⱏ', + 'ⱐ' => 'Ⱐ', + 'ⱑ' => 'Ⱑ', + 'ⱒ' => 'Ⱒ', + 'ⱓ' => 'Ⱓ', + 'ⱔ' => 'Ⱔ', + 'ⱕ' => 'Ⱕ', + 'ⱖ' => 'Ⱖ', + 'ⱗ' => 'Ⱗ', + 'ⱘ' => 'Ⱘ', + 'ⱙ' => 'Ⱙ', + 'ⱚ' => 'Ⱚ', + 'ⱛ' => 'Ⱛ', + 'ⱜ' => 'Ⱜ', + 'ⱝ' => 'Ⱝ', + 'ⱞ' => 'Ⱞ', + 'ⱡ' => 'Ⱡ', + 'ⱥ' => 'Ⱥ', + 'ⱦ' => 'Ⱦ', + 'ⱨ' => 'Ⱨ', + 'ⱪ' => 'Ⱪ', + 'ⱬ' => 'Ⱬ', + 'ⱳ' => 'Ⱳ', + 'ⱶ' => 'Ⱶ', + 'ⲁ' => 'Ⲁ', + 'ⲃ' => 'Ⲃ', + 'ⲅ' => 'Ⲅ', + 'ⲇ' => 'Ⲇ', + 'ⲉ' => 'Ⲉ', + 'ⲋ' => 'Ⲋ', + 'ⲍ' => 'Ⲍ', + 'ⲏ' => 'Ⲏ', + 'ⲑ' => 'Ⲑ', + 'ⲓ' => 'Ⲓ', + 'ⲕ' => 'Ⲕ', + 'ⲗ' => 'Ⲗ', + 'ⲙ' => 'Ⲙ', + 'ⲛ' => 'Ⲛ', + 'ⲝ' => 'Ⲝ', + 'ⲟ' => 'Ⲟ', + 'ⲡ' => 'Ⲡ', + 'ⲣ' => 'Ⲣ', + 'ⲥ' => 'Ⲥ', + 'ⲧ' => 'Ⲧ', + 'ⲩ' => 'Ⲩ', + 'ⲫ' => 'Ⲫ', + 'ⲭ' => 'Ⲭ', + 'ⲯ' => 'Ⲯ', + 'ⲱ' => 'Ⲱ', + 'ⲳ' => 'Ⲳ', + 'ⲵ' => 'Ⲵ', + 'ⲷ' => 'Ⲷ', + 'ⲹ' => 'Ⲹ', + 'ⲻ' => 'Ⲻ', + 'ⲽ' => 'Ⲽ', + 'ⲿ' => 'Ⲿ', + 'ⳁ' => 'Ⳁ', + 'ⳃ' => 'Ⳃ', + 'ⳅ' => 'Ⳅ', + 'ⳇ' => 'Ⳇ', + 'ⳉ' => 'Ⳉ', + 'ⳋ' => 'Ⳋ', + 'ⳍ' => 'Ⳍ', + 'ⳏ' => 'Ⳏ', + 'ⳑ' => 'Ⳑ', + 'ⳓ' => 'Ⳓ', + 'ⳕ' => 'Ⳕ', + 'ⳗ' => 'Ⳗ', + 'ⳙ' => 'Ⳙ', + 'ⳛ' => 'Ⳛ', + 'ⳝ' => 'Ⳝ', + 'ⳟ' => 'Ⳟ', + 'ⳡ' => 'Ⳡ', + 'ⳣ' => 'Ⳣ', + 'ⳬ' => 'Ⳬ', + 'ⳮ' => 'Ⳮ', + 'ⳳ' => 'Ⳳ', + 'ⴀ' => 'Ⴀ', + 'ⴁ' => 'Ⴁ', + 'ⴂ' => 'Ⴂ', + 'ⴃ' => 'Ⴃ', + 'ⴄ' => 'Ⴄ', + 'ⴅ' => 'Ⴅ', + 'ⴆ' => 'Ⴆ', + 'ⴇ' => 'Ⴇ', + 'ⴈ' => 'Ⴈ', + 'ⴉ' => 'Ⴉ', + 'ⴊ' => 'Ⴊ', + 'ⴋ' => 'Ⴋ', + 'ⴌ' => 'Ⴌ', + 'ⴍ' => 'Ⴍ', + 'ⴎ' => 'Ⴎ', + 'ⴏ' => 'Ⴏ', + 'ⴐ' => 'Ⴐ', + 'ⴑ' => 'Ⴑ', + 'ⴒ' => 'Ⴒ', + 'ⴓ' => 'Ⴓ', + 'ⴔ' => 'Ⴔ', + 'ⴕ' => 'Ⴕ', + 'ⴖ' => 'Ⴖ', + 'ⴗ' => 'Ⴗ', + 'ⴘ' => 'Ⴘ', + 'ⴙ' => 'Ⴙ', + 'ⴚ' => 'Ⴚ', + 'ⴛ' => 'Ⴛ', + 'ⴜ' => 'Ⴜ', + 'ⴝ' => 'Ⴝ', + 'ⴞ' => 'Ⴞ', + 'ⴟ' => 'Ⴟ', + 'ⴠ' => 'Ⴠ', + 'ⴡ' => 'Ⴡ', + 'ⴢ' => 'Ⴢ', + 'ⴣ' => 'Ⴣ', + 'ⴤ' => 'Ⴤ', + 'ⴥ' => 'Ⴥ', + 'ⴧ' => 'Ⴧ', + 'ⴭ' => 'Ⴭ', + 'ꙁ' => 'Ꙁ', + 'ꙃ' => 'Ꙃ', + 'ꙅ' => 'Ꙅ', + 'ꙇ' => 'Ꙇ', + 'ꙉ' => 'Ꙉ', + 'ꙋ' => 'Ꙋ', + 'ꙍ' => 'Ꙍ', + 'ꙏ' => 'Ꙏ', + 'ꙑ' => 'Ꙑ', + 'ꙓ' => 'Ꙓ', + 'ꙕ' => 'Ꙕ', + 'ꙗ' => 'Ꙗ', + 'ꙙ' => 'Ꙙ', + 'ꙛ' => 'Ꙛ', + 'ꙝ' => 'Ꙝ', + 'ꙟ' => 'Ꙟ', + 'ꙡ' => 'Ꙡ', + 'ꙣ' => 'Ꙣ', + 'ꙥ' => 'Ꙥ', + 'ꙧ' => 'Ꙧ', + 'ꙩ' => 'Ꙩ', + 'ꙫ' => 'Ꙫ', + 'ꙭ' => 'Ꙭ', + 'ꚁ' => 'Ꚁ', + 'ꚃ' => 'Ꚃ', + 'ꚅ' => 'Ꚅ', + 'ꚇ' => 'Ꚇ', + 'ꚉ' => 'Ꚉ', + 'ꚋ' => 'Ꚋ', + 'ꚍ' => 'Ꚍ', + 'ꚏ' => 'Ꚏ', + 'ꚑ' => 'Ꚑ', + 'ꚓ' => 'Ꚓ', + 'ꚕ' => 'Ꚕ', + 'ꚗ' => 'Ꚗ', + 'ꚙ' => 'Ꚙ', + 'ꚛ' => 'Ꚛ', + 'ꜣ' => 'Ꜣ', + 'ꜥ' => 'Ꜥ', + 'ꜧ' => 'Ꜧ', + 'ꜩ' => 'Ꜩ', + 'ꜫ' => 'Ꜫ', + 'ꜭ' => 'Ꜭ', + 'ꜯ' => 'Ꜯ', + 'ꜳ' => 'Ꜳ', + 'ꜵ' => 'Ꜵ', + 'ꜷ' => 'Ꜷ', + 'ꜹ' => 'Ꜹ', + 'ꜻ' => 'Ꜻ', + 'ꜽ' => 'Ꜽ', + 'ꜿ' => 'Ꜿ', + 'ꝁ' => 'Ꝁ', + 'ꝃ' => 'Ꝃ', + 'ꝅ' => 'Ꝅ', + 'ꝇ' => 'Ꝇ', + 'ꝉ' => 'Ꝉ', + 'ꝋ' => 'Ꝋ', + 'ꝍ' => 'Ꝍ', + 'ꝏ' => 'Ꝏ', + 'ꝑ' => 'Ꝑ', + 'ꝓ' => 'Ꝓ', + 'ꝕ' => 'Ꝕ', + 'ꝗ' => 'Ꝗ', + 'ꝙ' => 'Ꝙ', + 'ꝛ' => 'Ꝛ', + 'ꝝ' => 'Ꝝ', + 'ꝟ' => 'Ꝟ', + 'ꝡ' => 'Ꝡ', + 'ꝣ' => 'Ꝣ', + 'ꝥ' => 'Ꝥ', + 'ꝧ' => 'Ꝧ', + 'ꝩ' => 'Ꝩ', + 'ꝫ' => 'Ꝫ', + 'ꝭ' => 'Ꝭ', + 'ꝯ' => 'Ꝯ', + 'ꝺ' => 'Ꝺ', + 'ꝼ' => 'Ꝼ', + 'ꝿ' => 'Ꝿ', + 'ꞁ' => 'Ꞁ', + 'ꞃ' => 'Ꞃ', + 'ꞅ' => 'Ꞅ', + 'ꞇ' => 'Ꞇ', + 'ꞌ' => 'Ꞌ', + 'ꞑ' => 'Ꞑ', + 'ꞓ' => 'Ꞓ', + 'ꞗ' => 'Ꞗ', + 'ꞙ' => 'Ꞙ', + 'ꞛ' => 'Ꞛ', + 'ꞝ' => 'Ꞝ', + 'ꞟ' => 'Ꞟ', + 'ꞡ' => 'Ꞡ', + 'ꞣ' => 'Ꞣ', + 'ꞥ' => 'Ꞥ', + 'ꞧ' => 'Ꞧ', + 'ꞩ' => 'Ꞩ', + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + '𐐨' => '𐐀', + '𐐩' => '𐐁', + '𐐪' => '𐐂', + '𐐫' => '𐐃', + '𐐬' => '𐐄', + '𐐭' => '𐐅', + '𐐮' => '𐐆', + '𐐯' => '𐐇', + '𐐰' => '𐐈', + '𐐱' => '𐐉', + '𐐲' => '𐐊', + '𐐳' => '𐐋', + '𐐴' => '𐐌', + '𐐵' => '𐐍', + '𐐶' => '𐐎', + '𐐷' => '𐐏', + '𐐸' => '𐐐', + '𐐹' => '𐐑', + '𐐺' => '𐐒', + '𐐻' => '𐐓', + '𐐼' => '𐐔', + '𐐽' => '𐐕', + '𐐾' => '𐐖', + '𐐿' => '𐐗', + '𐑀' => '𐐘', + '𐑁' => '𐐙', + '𐑂' => '𐐚', + '𐑃' => '𐐛', + '𐑄' => '𐐜', + '𐑅' => '𐐝', + '𐑆' => '𐐞', + '𐑇' => '𐐟', + '𐑈' => '𐐠', + '𐑉' => '𐐡', + '𐑊' => '𐐢', + '𐑋' => '𐐣', + '𐑌' => '𐐤', + '𐑍' => '𐐥', + '𐑎' => '𐐦', + '𐑏' => '𐐧', + '𑣀' => '𑢠', + '𑣁' => '𑢡', + '𑣂' => '𑢢', + '𑣃' => '𑢣', + '𑣄' => '𑢤', + '𑣅' => '𑢥', + '𑣆' => '𑢦', + '𑣇' => '𑢧', + '𑣈' => '𑢨', + '𑣉' => '𑢩', + '𑣊' => '𑢪', + '𑣋' => '𑢫', + '𑣌' => '𑢬', + '𑣍' => '𑢭', + '𑣎' => '𑢮', + '𑣏' => '𑢯', + '𑣐' => '𑢰', + '𑣑' => '𑢱', + '𑣒' => '𑢲', + '𑣓' => '𑢳', + '𑣔' => '𑢴', + '𑣕' => '𑢵', + '𑣖' => '𑢶', + '𑣗' => '𑢷', + '𑣘' => '𑢸', + '𑣙' => '𑢹', + '𑣚' => '𑢺', + '𑣛' => '𑢻', + '𑣜' => '𑢼', + '𑣝' => '𑢽', + '𑣞' => '𑢾', + '𑣟' => '𑢿', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 00000000000..4b01b614a52 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_strlen')) { + define('MB_CASE_UPPER', 0); + define('MB_CASE_LOWER', 1); + define('MB_CASE_TITLE', 2); + + function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } + function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } + function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } + function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } + function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } + function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } + function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } + function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } + function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } + function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } + function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } + function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } + function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } + function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } + function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } + function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } + function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } + function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } + function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } + function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } + function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } + function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } + function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } + function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } + function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } + function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } + function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } + function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } + function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $v0, $a, $b, $c, $d, $e, $f); } +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 00000000000..355a0f88ff1 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + } +}